prop/test/microwaveprop/propagation/grid_test.exs
Graham McIntire 54cabdb609
fix(enrichment): mark OCONUS contacts hrrr_status :unavailable
Root cause of 14 stuck hrrr-queued contacts: hrrr_point_rs's CONUS-only
grid silently writes zero profiles for out-of-bounds points (UK,
Winnipeg, Alberta, >50° lat), but the fetch task still completes
successfully. ContactWeatherEnqueueWorker's hrrr_placeholder_jobs
always emitted a queued placeholder for any contact with a pos1,
which meant mark_hrrr_status! re-flagged :queued every backfill tick
forever.

Introduce Propagation.Grid.contains?/1 as the canonical in-CONUS
check (inclusive on all four edges, nil-safe). Use it in two places
of ContactWeatherEnqueueWorker:

- hrrr_placeholder_jobs/1 now returns [] for OCONUS contacts, same
  as pre-2014 / already-complete ones.
- mark_hrrr_status!/3 empty-list clause now branches on Grid.contains?
  first (→ :unavailable) before falling through to the existing
  NARR-coverage / :complete logic.

Next backfill tick will flip the 14 stuck OCONUS contacts in prod to
:unavailable automatically. The remaining 13 "CONUS" stuck ones are
right at the 50° edge (lat=50.1875 rounds outside the box) — same
reconciliation path.

Coverage additions:
- Grid.contains?/1 — corner-inclusive, OCONUS positive/negative
  fixtures based on the actual stuck-contact pos1 values, nil/missing-
  key/nil-value handling.
- enqueue_for_contact regression tests: pos1 in the UK and pos1 just
  north of the lat cap both land at :unavailable, not :queued.

Suite: 2,416 tests + 159 properties (was 2,409 + 159); credo strict
clean.
2026-04-23 14:19:23 -05:00

117 lines
3.6 KiB
Elixir

defmodule Microwaveprop.Propagation.GridTest do
use ExUnit.Case, async: true
alias Microwaveprop.Propagation.Grid
describe "conus_points/0" do
test "generates grid at 0.125 degree resolution" do
points = Grid.conus_points()
assert length(points) > 5000
assert length(points) < 100_000
end
test "all points within CONUS bounds" do
Enum.each(Grid.conus_points(), fn {lat, lon} ->
assert lat >= 25.0 and lat <= 50.0
assert lon >= -125.0 and lon <= -66.0
end)
end
test "points are on 0.125 degree grid" do
Enum.each(Grid.conus_points(), fn {lat, lon} ->
assert Float.round(lat * 8, 0) == lat * 8
assert Float.round(lon * 8, 0) == lon * 8
end)
end
test "first point is at SW corner" do
[{lat, lon} | _] = Grid.conus_points()
assert lat == 25.0
assert lon == -125.0
end
test "last point is at NE corner" do
points = Grid.conus_points()
{lat, lon} = List.last(points)
assert lat == 50.0
assert lon == -66.0
end
end
describe "step/0" do
test "returns 0.125" do
assert Grid.step() == 0.125
end
end
describe "bounds/0" do
test "returns CONUS bounding box" do
bounds = Grid.bounds()
assert bounds.lat_min == 25.0
assert bounds.lat_max == 50.0
assert bounds.lon_min == -125.0
assert bounds.lon_max == -66.0
end
end
describe "contains?/1" do
test "true for DFW (firmly inside the box)" do
assert Grid.contains?(%{"lat" => 32.9, "lon" => -97.0})
end
test "true for every corner of the CONUS bounding box (inclusive)" do
assert Grid.contains?(%{"lat" => 25.0, "lon" => -125.0})
assert Grid.contains?(%{"lat" => 50.0, "lon" => -66.0})
assert Grid.contains?(%{"lat" => 25.0, "lon" => -66.0})
assert Grid.contains?(%{"lat" => 50.0, "lon" => -125.0})
end
test "false for OCONUS positions (UK, Winnipeg, Alberta, etc.)" do
# 27 stuck hrrr-queued contacts in prod landed on points like
# these. Add them as regression fixtures.
refute Grid.contains?(%{"lat" => 51.4, "lon" => 0.47})
refute Grid.contains?(%{"lat" => 51.39, "lon" => -114.04})
refute Grid.contains?(%{"lat" => 50.1875, "lon" => -97.625})
end
test "false for nil pos or missing keys" do
refute Grid.contains?(nil)
refute Grid.contains?(%{})
refute Grid.contains?(%{"lat" => 32.9})
refute Grid.contains?(%{"lon" => -97.0})
end
test "false when lat or lon is nil" do
refute Grid.contains?(%{"lat" => nil, "lon" => -97.0})
refute Grid.contains?(%{"lat" => 32.9, "lon" => nil})
end
end
describe "wgrib2_grid_spec/0" do
test "lon_start / lat_start match the bounding box SW corner" do
spec = Grid.wgrib2_grid_spec()
assert spec.lon_start == -125.0
assert spec.lat_start == 25.0
end
test "grid step matches Grid.step/0 on both axes" do
spec = Grid.wgrib2_grid_spec()
assert spec.lon_step == Grid.step()
assert spec.lat_step == Grid.step()
end
test "lon_count and lat_count span the full CONUS box inclusive" do
spec = Grid.wgrib2_grid_spec()
# (lon_max - lon_min) / step + 1 = (-66 - -125) / 0.125 + 1 = 473
assert spec.lon_count == 473
# (lat_max - lat_min) / step + 1 = (50 - 25) / 0.125 + 1 = 201
assert spec.lat_count == 201
end
test "total cells in wgrib2_grid_spec matches conus_points length" do
spec = Grid.wgrib2_grid_spec()
points = Grid.conus_points()
assert length(points) == spec.lon_count * spec.lat_count
end
end
end