prop/test/microwaveprop_web/live/weather_ca_map_live_test.exs
Graham McIntire 581955bd69
Some checks failed
Build and Push / Build and Push Docker Image (push) Has been cancelled
fix: prevent contacts_dedup_idx collisions in parallel async tests
Create shared ContactsFixtures module with globally-unique qso_timestamp
seconds to prevent unique-constraint violations when async test modules
run in parallel and insert contacts with identical dedup-key columns.
The qso_timestamp column is timestamp(0), so microsecond offsets were
truncated — use System.unique_integer monotonic seconds instead.

Also make count-asserting tests resilient to sandbox-leaked contacts
from prior tests by using >= assertions or status-based checks rather
than exact counts.

Includes automated DateTime.add → DateTime.shift migration from
mix format.
2026-08-04 17:05:16 -05:00

89 lines
3 KiB
Elixir

defmodule MicrowavepropWeb.WeatherCaMapLiveTest do
# async: false because we touch the shared scalar / profile dirs.
use MicrowavepropWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias Microwaveprop.Propagation.ProfilesFile
alias Microwaveprop.Weather.GridCache
alias Microwaveprop.Weather.ScalarFile
setup do
GridCache.clear()
on_exit(fn ->
GridCache.clear()
ProfilesFile.prune_older_than(~U[2099-12-31 23:59:59Z])
end)
:ok
end
test "mounts at /weather-ca with Canadian default viewport", %{conn: conn} do
{:ok, _lv, html} = live(conn, ~p"/weather-ca")
# Default viewport is centered roughly on central Canada at a low
# zoom — different from /weather's DFW@z7. The exact numbers are
# asserted so a routing accident (mounting WeatherMapLive at this
# path by mistake) is caught.
assert html =~ ~s(data-initial-lat="55.0")
assert html =~ ~s(data-initial-lon="-100.0")
assert html =~ ~s(data-initial-zoom="4")
end
test "renders the weather map element with data-source=hrdps", %{conn: conn} do
{:ok, _lv, html} = live(conn, ~p"/weather-ca")
# The JS hook reads data-source and appends &source=hrdps to its cell
# fetches. Without this attribute the page would silently fall back
# to HRRR-only reads and never show Canadian data.
assert html =~ ~s(data-source="hrdps")
end
test "viewport_changed event push_patches /weather-ca, not /weather", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/weather-ca")
render_hook(lv, "viewport_changed", %{"lat" => 60.0, "lon" => -110.0, "zoom" => 5})
assert_patch(lv, "/weather-ca?layer=temperature&lat=60.0&lon=-110.0&zoom=5")
end
test "select_layer push_patches a /weather-ca URL", %{conn: conn} do
{:ok, lv, _html} = live(conn, ~p"/weather-ca")
render_hook(lv, "select_layer", %{"layer" => "pwat"})
assert_patch(lv, "/weather-ca?layer=pwat&lat=55.0&lon=-100.0&zoom=4")
end
test "shows HRDPS valid_times even when several hours old (no 1h cutoff)", %{conn: conn} do
# HRDPS publishes 4x/day with ~3-4h latency, plus our chain step
# takes ~13 min. The 1-hour cutoff /weather uses would routinely
# leave /weather-ca empty between fresh chain steps. Hand-write a
# 4-hour-old HRDPS chunk and confirm it lands in the timeline.
now =
DateTime.utc_now()
|> DateTime.truncate(:second)
|> Map.put(:minute, 0)
|> Map.put(:second, 0)
stale_t = DateTime.shift(now, hour: -4)
hrdps_dir = ScalarFile.dir_for_hrdps(stale_t)
File.mkdir_p!(hrdps_dir)
payload =
Msgpax.pack!(
[%{"lat" => 53.0, "lon" => -60.0, "valid_time" => DateTime.to_iso8601(stale_t), "temperature" => 8.0}],
iodata: false
)
File.write!(Path.join(hrdps_dir, "10_-12.mp.gz"), :zlib.gzip(payload))
on_exit(fn -> File.rm_rf!(hrdps_dir) end)
{:ok, _lv, html} = live(conn, ~p"/weather-ca")
iso = DateTime.to_iso8601(stale_t)
assert html =~ iso, "4-hour-old HRDPS valid_time should appear in /weather-ca timeline"
end
end