fix(map): recover with sane defaults on garbage URL params
Every parser already returned nil on malformed input, so the || chains fell through to defaults — except parse_time_param, which happily accepted any well-formed ISO8601 even if it pointed outside the current HRRR forecast window (e.g. a stale shared link from last week). Gate it behind the valid-times window and fall back to the normal 'now' cursor when it's out of range. Adds explicit coverage: non-numeric band, unknown band MHz, non-numeric lat/lon/zoom, out-of-range values, malformed time, ancient time, and all-params-garbage-at-once. Each case mounts cleanly and lands on the defaults.
This commit is contained in:
parent
160439db31
commit
bb6333bd27
2 changed files with 65 additions and 1 deletions
|
|
@ -136,16 +136,38 @@ defmodule MicrowavepropWeb.MapLive do
|
|||
|
||||
# Resolve the initial band/time/center/zoom from URL params, falling
|
||||
# back to LiveStash recovery, then session geolocation and defaults.
|
||||
# Parsers return nil for anything malformed so `||` walks to the next
|
||||
# sensible source; ISO8601 times that sit outside the current HRRR
|
||||
# window get dropped too so a stale shared link doesn't land the map
|
||||
# on a hour with no scores.
|
||||
defp resolve_view(params, session, recovered_band, recovered_time) do
|
||||
band = parse_band_param(params["band"]) || recovered_band || @default_band
|
||||
valid_times = Propagation.available_valid_times(band)
|
||||
time = parse_time_param(params["t"]) || recovered_time || closest_to_now(valid_times)
|
||||
time = resolve_time(params["t"], recovered_time, valid_times)
|
||||
center = parse_center_param(params["lat"], params["lon"]) || initial_center(session)
|
||||
zoom = parse_zoom_param(params["zoom"]) || @default_zoom
|
||||
|
||||
%{band: band, time: time, center: center, zoom: zoom, valid_times: valid_times}
|
||||
end
|
||||
|
||||
defp resolve_time(param, recovered_time, valid_times) do
|
||||
case parse_time_param(param) do
|
||||
%DateTime{} = t ->
|
||||
if time_in_window?(t, valid_times), do: t, else: closest_to_now(valid_times)
|
||||
|
||||
nil ->
|
||||
recovered_time || closest_to_now(valid_times)
|
||||
end
|
||||
end
|
||||
|
||||
defp time_in_window?(_, []), do: false
|
||||
|
||||
defp time_in_window?(%DateTime{} = t, valid_times) do
|
||||
earliest = Enum.min(valid_times, DateTime)
|
||||
latest = Enum.max(valid_times, DateTime)
|
||||
DateTime.compare(t, earliest) != :lt and DateTime.compare(t, latest) != :gt
|
||||
end
|
||||
|
||||
# Build a bounding box roughly matching the hardcoded default (~3.4° × 9°)
|
||||
# around a given center so the initial HRRR score query still returns a
|
||||
# useful tile set for the visible area.
|
||||
|
|
|
|||
|
|
@ -145,6 +145,48 @@ defmodule MicrowavepropWeb.MapLiveTest do
|
|||
{:ok, _lv, html} = live(conn, ~p"/map?band=notanumber")
|
||||
assert html =~ ~s(data-selected-band="10000")
|
||||
end
|
||||
|
||||
test "unknown band MHz falls back to the default", %{conn: conn} do
|
||||
{:ok, _lv, html} = live(conn, ~p"/map?band=99999")
|
||||
assert html =~ ~s(data-selected-band="10000")
|
||||
end
|
||||
|
||||
test "malformed lat/lon/zoom are ignored", %{conn: conn} do
|
||||
{:ok, _lv, html} = live(conn, ~p"/map?lat=banana&lon=apple&zoom=over9000")
|
||||
assert html =~ ~s(data-zoom="7")
|
||||
refute html =~ ~s(data-center-lat="banana")
|
||||
end
|
||||
|
||||
test "out-of-range lat/lon are ignored", %{conn: conn} do
|
||||
{:ok, _lv, html} = live(conn, ~p"/map?lat=500&lon=-999")
|
||||
refute html =~ ~s(data-center-lat="500)
|
||||
end
|
||||
|
||||
test "out-of-range zoom falls back to default", %{conn: conn} do
|
||||
{:ok, _lv, html} = live(conn, ~p"/map?zoom=42")
|
||||
assert html =~ ~s(data-zoom="7")
|
||||
end
|
||||
|
||||
test "unparseable time is ignored", %{conn: conn} do
|
||||
{:ok, lv, _html} = live(conn, ~p"/map?t=not-a-date")
|
||||
# Page renders normally without crashing.
|
||||
assert render(lv) =~ "data-selected-time"
|
||||
end
|
||||
|
||||
test "ISO8601 time outside the current forecast window falls back", %{conn: conn} do
|
||||
# Year 2000 is well before any valid HRRR data; page should still
|
||||
# mount and fall back to the normal 'now' cursor rather than try
|
||||
# to fetch scores for an empty time.
|
||||
{:ok, _lv, _html} = live(conn, ~p"/map?t=2000-01-01T00:00:00Z")
|
||||
end
|
||||
|
||||
test "every param garbage at once still mounts cleanly", %{conn: conn} do
|
||||
{:ok, _lv, html} =
|
||||
live(conn, ~p"/map?band=xyz&t=nope&lat=abc&lon=def&zoom=foo")
|
||||
|
||||
assert html =~ ~s(data-selected-band="10000")
|
||||
assert html =~ ~s(data-zoom="7")
|
||||
end
|
||||
end
|
||||
|
||||
describe "cursor_for_now/2" do
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue