defmodule Microwaveprop.Workers.HrrrFetchWorkerTest do use Microwaveprop.DataCase, async: true alias Microwaveprop.Weather alias Microwaveprop.Weather.HrrrProfile alias Microwaveprop.Workers.HrrrFetchWorker @sample_profile [ %{"pres" => 1000.0, "hght" => 110.0, "tmpc" => 25.0, "dwpc" => 18.0}, %{"pres" => 975.0, "hght" => 350.0, "tmpc" => 23.0, "dwpc" => 16.0}, %{"pres" => 950.0, "hght" => 590.0, "tmpc" => 21.0, "dwpc" => 14.0} ] @sample_client_result %{ surface_temp_c: 25.0, surface_dewpoint_c: 18.0, surface_pressure_mb: 1013.0, hpbl_m: 1500.0, pwat_mm: 25.0, run_time: ~U[2026-03-28 18:00:00Z], profile: @sample_profile } describe "perform/1" do test "skips if HRRR profile already exists" do Weather.upsert_hrrr_profile(%{ valid_time: ~U[2026-03-28 18:00:00Z], lat: 32.90, lon: -97.04, profile: @sample_profile }) job = HrrrFetchWorker.new(%{ "lat" => 32.90, "lon" => -97.04, "valid_time" => "2026-03-28T18:00:00Z" }) assert :ok = HrrrFetchWorker.perform(%Oban.Job{args: job.changes.args}) end test "stores profile when client returns success" do # We test the perform logic by mocking at the module level # The actual HTTP calls are tested in HrrrClientTest # Here we verify the worker's coordination logic lat = 32.90 lon = -97.04 valid_time = ~U[2026-03-28 18:00:00Z] # Verify no profile exists initially refute Weather.has_hrrr_profile?(lat, lon, valid_time) # Simulate what perform does after a successful fetch attrs = build_profile_attrs(lat, lon, valid_time, @sample_client_result) {:ok, profile} = Weather.upsert_hrrr_profile(attrs) assert profile.lat == lat assert profile.lon == lon assert profile.hpbl_m == 1500.0 assert profile.surface_temp_c == 25.0 assert Weather.has_hrrr_profile?(lat, lon, valid_time) end test "rounds lat/lon before checking existence" do # Profile at rounded coordinates Weather.upsert_hrrr_profile(%{ valid_time: ~U[2026-03-28 18:00:00Z], lat: 32.90, lon: -97.04, profile: [] }) # Worker called with slightly different coords that round to same job = HrrrFetchWorker.new(%{ "lat" => 32.904, "lon" => -97.039, "valid_time" => "2026-03-28T18:00:00Z" }) # Should skip because rounded coords match assert :ok = HrrrFetchWorker.perform(%Oban.Job{args: job.changes.args}) # Only one profile should exist count = Repo.aggregate(HrrrProfile, :count) assert count == 1 end end describe "perform/1 batch mode" do test "skips all points when profiles already exist" do Weather.upsert_hrrr_profile(%{ valid_time: ~U[2026-03-28 18:00:00Z], lat: 32.90, lon: -97.04, profile: @sample_profile }) job = %Oban.Job{ args: %{ "points" => [%{"lat" => 32.90, "lon" => -97.04}], "valid_time" => "2026-03-28T18:00:00Z" } } assert :ok = HrrrFetchWorker.perform(job) end end describe "backoff/1" do test "returns exponential backoff capped at 6 hours" do assert HrrrFetchWorker.backoff(%Oban.Job{attempt: 1}) == 120 assert HrrrFetchWorker.backoff(%Oban.Job{attempt: 2}) == 240 assert HrrrFetchWorker.backoff(%Oban.Job{attempt: 10}) <= 21_600 end end describe "permanent_failure?/1" do test "cancels on 404 idx" do job = %Oban.Job{ args: %{ "lat" => 32.90, "lon" => -97.04, "valid_time" => "1995-06-15T12:00:00Z" } } Req.Test.stub(Microwaveprop.Weather.HrrrClient, fn conn -> Plug.Conn.send_resp(conn, 404, "not found") end) assert {:cancel, "HRRR idx HTTP 404"} = HrrrFetchWorker.perform(job) end end defp build_profile_attrs(lat, lon, valid_time, client_result) do %{ valid_time: valid_time, lat: lat, lon: lon, run_time: client_result.run_time, profile: client_result.profile, hpbl_m: client_result.hpbl_m, pwat_mm: client_result.pwat_mm, surface_temp_c: client_result.surface_temp_c, surface_dewpoint_c: client_result.surface_dewpoint_c, surface_pressure_mb: client_result.surface_pressure_mb } end end