prop/test/microwaveprop/workers/propagation_grid_worker_test.exs
Graham McIntire 65693ed415
feat(prop): Phase 2 cutover — Rust owns f01–f18
Rust `prop-grid-rs` has been validated end-to-end on talos5 on the
streamed-bands image (main-1776635096-6d91461): real chain steps
complete in ~7 s each and the peak-heap refactor landed.

Changes:
- `PropagationGridWorker.seed_chain` now enqueues a single f00 Oban
  job instead of f00–f18. The f00 analysis-hour keeps its enrichment
  (native-level duct, NEXRAD composite, commercial-link boost,
  ProfilesFile write) and the GridCache broadcast.
- `GridTaskEnqueuer.seed/1` is called again so grid_tasks gets the
  18 forecast-hour rows the Rust worker claims.
- Test asserts only one Oban job is enqueued and the matching 18
  grid_tasks rows land in the DB.
- `deployment-grid-rs.yaml` flips `PROP_SCORES_DIR` from
  `/data/scores_shadow` to `/data/scores` so Rust writes to the live
  score tree. No file collision — Elixir only writes the f00 files.

The hot pod memory shrink (6 Gi → 1 Gi) is deferred until one full
hourly cycle runs clean under the new split.
2026-04-19 16:51:14 -05:00

79 lines
2.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.PropagationGridWorkerTest do
@moduledoc """
Tests the chain-orchestration behavior of PropagationGridWorker.
The worker processes forecast hours f00f18 across the CONUS grid,
but a single full sweep takes ~2 hours of wall time — longer than a
typical pod restart window. To survive deploys, the worker processes
ONE forecast hour per `perform/1` call and enqueues the next hour as
a fresh Oban job. The tests here cover the dispatch + chain logic
without mocking the full HRRR / scoring stack.
"""
use Microwaveprop.DataCase, async: false
use Oban.Testing, repo: Microwaveprop.Repo
alias Microwaveprop.Workers.MrmsFetchWorker
alias Microwaveprop.Workers.PropagationGridWorker
alias Microwaveprop.Workers.PropagationPruneWorker
describe "queue priority" do
test "PropagationGridWorker runs at the highest priority on :propagation" do
assert PropagationGridWorker.__opts__()[:priority] == 0
end
test "MrmsFetchWorker and PropagationPruneWorker yield to the grid chain" do
# Same :propagation queue — must be lower priority so hourly chain
# steps jump ahead of a MRMS or pruner backlog.
assert MrmsFetchWorker.__opts__()[:priority] > 0
assert PropagationPruneWorker.__opts__()[:priority] > 0
end
end
describe "compute_scores_algorithm/3" do
test "accepts a map of {{lat, lon} => profile} without raising" do
# grid_data is a map keyed by {lat, lon}, not a list.
# A regression guard against using length/1 on the map.
valid_time = ~U[2026-04-19 15:00:00Z]
assert [] =
PropagationGridWorker.compute_scores_algorithm(
%{},
valid_time,
false
)
end
end
describe "perform/1 — chain seeding (empty args)" do
test "enqueues only f00 in Oban; f01..f18 go to grid_tasks for Rust" do
Oban.Testing.with_testing_mode(:manual, fn ->
import Ecto.Query
assert :ok = PropagationGridWorker.perform(%Oban.Job{args: %{}})
jobs = all_enqueued(worker: PropagationGridWorker)
assert length(jobs) == 1
[job] = jobs
assert job.args["forecast_hour"] == 0
# run_time is normalized to top-of-hour UTC.
{:ok, dt, _} = DateTime.from_iso8601(job.args["run_time"])
assert dt.minute == 0
assert dt.second == 0
assert DateTime.before?(dt, DateTime.utc_now())
# The Rust worker picks up f01..f18 from the grid_tasks table.
task_fhs =
Microwaveprop.Repo.all(
from t in "grid_tasks",
where: t.run_time == ^DateTime.truncate(dt, :second),
select: t.forecast_hour,
order_by: t.forecast_hour
)
assert task_fhs == Enum.to_list(1..18)
end)
end
end
end