prop/test/microwaveprop/workers/ionosphere_fetch_worker_test.exs
Graham McIntire bbf51544e1
test: coverage round 2 (82.05% → 83.96%) + 23 new property tests
77 unit tests + 23 property tests across four parallel agents.

- ContactLive.Show (72.01% → ~75%): every factor-note branch
  (humidity/time/td/refractivity/pressure/season/pwat), all
  propagation_mechanism classifications, apply_admin_edit/owner_edit/
  submit_user_edit error paths, internal_network? IP shapes, expanded
  sounding skew-T, composite-score [0,100] property.

- PathLive + BeaconLive.Show + Weather: coordinate-pair resolver,
  invalid grid, empty-DB edge cases for iemre_for_*, narr_for_*,
  nearest_native_duct_*, find_nearest_rtma, recent_surface_obs;
  properties for round_to_hrrr_grid/round_to_iemre_grid/band
  round-trip/beacon-id URL round-trip.

- Viewshed + Duct + SoundingParams: find_reach_km edge cases,
  destination_point, effective_reach_km fractions, detect_ducts
  trailing-duct + guard branches; 13 properties including flat-
  terrain-fully-visible, thicker-duct-lower-freq, M-profile
  monotonicity, feature-vector deterministic length.

- Workers + Mix tasks: GefsFetchWorker 500/429/403 + malformed
  ISO8601, HrrrNativeGridWorker snap-to-3-decimals + empty DB,
  IonosphereFetchWorker 404 + non-tabular body, RadarBackfill
  --dry-run + --year + --limit, NexradBackfill --limit 0, Backtest
  --feature + --all; year-filter property covering 2015..2026.

170 → 205 properties, 2666 → 2743 tests, 0 failures.
2026-04-24 10:15:37 -05:00

111 lines
3.8 KiB
Elixir
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

defmodule Microwaveprop.Workers.IonosphereFetchWorkerTest do
use Microwaveprop.DataCase, async: false
use Oban.Testing, repo: Microwaveprop.Repo
alias Microwaveprop.Ionosphere
alias Microwaveprop.Ionosphere.GiroClient
alias Microwaveprop.Ionosphere.Observation
alias Microwaveprop.Workers.IonosphereFetchWorker
@sample File.read!("test/fixtures/giro/mhj45_sample.txt")
describe "perform/1" do
test "pulls each configured station and upserts the parsed rows" do
Req.Test.stub(GiroClient, fn conn ->
assert conn.method == "GET"
params = Plug.Conn.fetch_query_params(conn).query_params
assert params["ursiCode"] in ~w(MHJ45 AL945)
Plug.Conn.resp(conn, 200, @sample)
end)
assert :ok = perform_job(IonosphereFetchWorker, %{})
# 25 observations per station × 2 stations = 50 rows (fixture has 25
# data lines; upserts go to each station code separately).
assert Repo.aggregate(Observation, :count, :id) == 50
assert Ionosphere.latest_observation("MHJ45")
assert Ionosphere.latest_observation("AL945")
end
test "survives a station's fetch error and still ingests the others" do
Req.Test.stub(GiroClient, fn conn ->
params = Plug.Conn.fetch_query_params(conn).query_params
if params["ursiCode"] == "MHJ45" do
Plug.Conn.resp(conn, 500, "boom")
else
Plug.Conn.resp(conn, 200, @sample)
end
end)
assert :ok = perform_job(IonosphereFetchWorker, %{})
# MHJ45 failed, AL945 ingested.
assert Ionosphere.latest_observation("MHJ45") == nil
assert Ionosphere.latest_observation("AL945")
end
test "tolerates an HTTP transport failure with :ok + no rows inserted" do
Req.Test.stub(GiroClient, fn conn ->
Req.Test.transport_error(conn, :timeout)
end)
assert :ok = perform_job(IonosphereFetchWorker, %{})
assert Repo.aggregate(Observation, :count, :id) == 0
end
end
describe "stations/0" do
test "exposes a non-empty list of CONUS ionosonde stations" do
stations = IonosphereFetchWorker.stations()
assert is_list(stations)
assert length(stations) >= 2
# Every station entry has the shape the fetcher expects, with a
# plausible CONUS lat/lon envelope.
for s <- stations do
assert is_binary(s.code)
assert is_binary(s.name)
assert is_float(s.lat) or is_integer(s.lat)
assert is_float(s.lon) or is_integer(s.lon)
assert s.lat > 20.0 and s.lat < 75.0
assert s.lon > -170.0 and s.lon < -60.0
end
end
end
describe "perform/1 additional HTTP branches" do
test "tolerates an HTTP 404 with :ok and zero rows inserted" do
# 404 is returned by GIRO for stations with no data in the window.
# The worker should treat it as a per-station no-op and return :ok.
Req.Test.stub(GiroClient, fn conn ->
Plug.Conn.send_resp(conn, 404, "not found")
end)
assert :ok = perform_job(IonosphereFetchWorker, %{})
assert Repo.aggregate(Observation, :count, :id) == 0
end
test "tolerates a non-tabular response body gracefully" do
# A body that doesn't match the expected comment/columns format
# should parse to zero rows — no crash, no inserts.
Req.Test.stub(GiroClient, fn conn ->
Plug.Conn.resp(conn, 200, "not a tabular response at all")
end)
assert :ok = perform_job(IonosphereFetchWorker, %{})
assert Repo.aggregate(Observation, :count, :id) == 0
end
test "tolerates an empty-body 200 response" do
Req.Test.stub(GiroClient, fn conn ->
Plug.Conn.resp(conn, 200, "")
end)
assert :ok = perform_job(IonosphereFetchWorker, %{})
assert Repo.aggregate(Observation, :count, :id) == 0
end
end
end