prop/test/microwaveprop/weather/rebatch_asos_test.exs
Graham McIntire f644d1796f
test: push coverage 79.64% -> 80.04% across multiple modules
New test files: RoverPlanning.Station, LiveStashGuard, RebatchAsos,
RoverMissionReconcileWorker, RoverPathProfileWorker. Extended existing
suites for PathCompute (resolve_location, build_ionosphere_readout),
HrdpsClient (DEPR fallback, missing-level skip), NexradClient
(fetch_rain_cells HTTP error paths), UserHomeQthLookupWorker (error +
Maidenhead-fallback paths), RoverPlanning.Path (statuses/0 + schema).

3512 tests passing.
2026-05-08 08:42:03 -05:00

212 lines
6.2 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.Weather.RebatchAsosTest do
use Microwaveprop.DataCase, async: false
import ExUnit.CaptureIO
alias Microwaveprop.Repo
alias Microwaveprop.Weather.RebatchAsos
describe "run/1 — no pending jobs" do
test "returns zeros and prints a no-op message when no jobs exist" do
output =
capture_io(fn ->
assert summary = RebatchAsos.run()
send(self(), summary)
end)
assert_received %{found: 0, windows: 0, inserted: 0, cancelled: 0}
assert output =~ "No pending"
end
end
describe "to_day_jobs/1 — no pending jobs" do
test "returns zeros when no asos/asos_batch jobs exist" do
output =
capture_io(fn ->
assert summary = RebatchAsos.to_day_jobs()
send(self(), summary)
end)
assert_received %{source_jobs: 0, unique_day_keys: 0, inserted: 0, cancelled: 0}
assert output =~ "No pending"
end
end
describe "run/1 — dry-run with pending jobs" do
test "groups by (start_dt, end_dt) and reports without mutating" do
insert_asos_job!(%{
"fetch_type" => "asos",
"station_id" => 1,
"station_code" => "KDFW",
"start_dt" => "2026-04-18T00:00:00Z",
"end_dt" => "2026-04-18T04:00:00Z"
})
insert_asos_job!(%{
"fetch_type" => "asos",
"station_id" => 2,
"station_code" => "KDAL",
"start_dt" => "2026-04-18T00:00:00Z",
"end_dt" => "2026-04-18T04:00:00Z"
})
output =
capture_io(fn ->
summary = RebatchAsos.run(dry_run: true)
send(self(), summary)
end)
assert_received %{found: 2, windows: 1, inserted: 0, cancelled: 0}
assert output =~ "[dry-run]"
assert output =~ "2026-04-18T00:00:00Z"
# Original two jobs should still be available; nothing was cancelled.
assert pending_asos_count() == 2
end
end
describe "to_day_jobs/1 — dry-run with pending asos jobs" do
test "expands one asos job into a per-date day-key plan" do
# A single 4-hour window inside one UTC day → 1 unique (station, date).
insert_asos_job!(%{
"fetch_type" => "asos",
"station_id" => 1,
"station_code" => "KDFW",
"start_dt" => "2026-04-18T00:00:00Z",
"end_dt" => "2026-04-18T04:00:00Z"
})
output =
capture_io(fn ->
summary = RebatchAsos.to_day_jobs(dry_run: true)
send(self(), summary)
end)
assert_received %{source_jobs: 1, unique_day_keys: 1, inserted: 0, cancelled: 0}
assert output =~ "[dry-run]"
assert output =~ "KDFW"
assert output =~ "2026-04-18"
end
test "expands an asos_batch into one day-key per (station, date)" do
insert_asos_job!(%{
"fetch_type" => "asos_batch",
"station_ids" => [1, 2],
"station_codes" => ["KDFW", "KDAL"],
"start_dt" => "2026-04-18T00:00:00Z",
"end_dt" => "2026-04-18T04:00:00Z"
})
capture_io(fn ->
summary = RebatchAsos.to_day_jobs(dry_run: true)
send(self(), summary)
end)
# One asos_batch with 2 stations × 1 date → 2 unique (station, date) keys.
assert_received %{source_jobs: 1, unique_day_keys: 2}
end
test "asos jobs spanning two UTC dates produce two day-keys per station" do
insert_asos_job!(%{
"fetch_type" => "asos",
"station_id" => 1,
"station_code" => "KDFW",
"start_dt" => "2026-04-18T22:00:00Z",
"end_dt" => "2026-04-19T02:00:00Z"
})
capture_io(fn ->
summary = RebatchAsos.to_day_jobs(dry_run: true)
send(self(), summary)
end)
# 1 station × 2 dates → 2 unique keys.
assert_received %{source_jobs: 1, unique_day_keys: 2}
end
test "asos jobs missing window timestamps contribute zero day-keys" do
insert_asos_job!(%{
"fetch_type" => "asos",
"station_id" => 1,
"station_code" => "KDFW",
"start_dt" => nil,
"end_dt" => nil
})
capture_io(fn ->
summary = RebatchAsos.to_day_jobs(dry_run: true)
send(self(), summary)
end)
# Job is found but expand_tuples returns [] for nil window.
assert_received %{source_jobs: 1, unique_day_keys: 0}
end
end
describe "run/1 — apply (non-dry-run)" do
test "inserts an asos_batch job and cancels the source rows" do
Oban.Testing.with_testing_mode(:manual, fn ->
insert_asos_job!(%{
"fetch_type" => "asos",
"station_id" => 1,
"station_code" => "KDFW",
"start_dt" => "2026-04-18T00:00:00Z",
"end_dt" => "2026-04-18T04:00:00Z"
})
capture_io(fn ->
summary = RebatchAsos.run()
send(self(), summary)
end)
end)
assert_received %{found: 1, windows: 1, inserted: inserted, cancelled: cancelled}
assert inserted >= 1
assert cancelled >= 1
end
end
describe "to_day_jobs/1 — apply (non-dry-run)" do
test "inserts asos_day jobs and cancels source rows" do
Oban.Testing.with_testing_mode(:manual, fn ->
insert_asos_job!(%{
"fetch_type" => "asos",
"station_id" => 1,
"station_code" => "KDFW",
"start_dt" => "2026-04-18T00:00:00Z",
"end_dt" => "2026-04-18T04:00:00Z"
})
capture_io(fn ->
summary = RebatchAsos.to_day_jobs()
send(self(), summary)
end)
end)
assert_received %{source_jobs: 1, unique_day_keys: 1, inserted: inserted, cancelled: cancelled}
assert inserted >= 1
assert cancelled >= 1
end
end
# ── helpers ───────────────────────────────────────────────────────────
defp insert_asos_job!(args) do
{:ok, job} =
args
|> Oban.Job.new(worker: "Microwaveprop.Workers.WeatherFetchWorker", queue: "weather")
|> Repo.insert()
job
end
defp pending_asos_count do
Repo.aggregate(
Ecto.Query.from(j in Oban.Job,
where:
j.worker == "Microwaveprop.Workers.WeatherFetchWorker" and
j.state in ["available", "scheduled", "retryable"]
),
:count
)
end
end