test: http_get injection for GefsClient + HrrrClient

- GefsClient (73%): http_get injection for fetch_idx + grib download,
  tests for timeout + HTTP error paths, dewpoint_from_rh edge cases
- HrrrClient: http_get injection for do_fetch_idx + range downloads
- GefsClient fetch_grid_profiles error-path tests with stubbed HTTP

Coverage: 79.68%
This commit is contained in:
Graham McIntire 2026-05-07 16:44:40 -05:00
parent cea2507fac
commit 06a54075f8
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
3 changed files with 63 additions and 4 deletions

View file

@ -236,8 +236,13 @@ defmodule Microwaveprop.Weather.GefsClient do
":(#{vars}):" ":(#{vars}):"
end end
defp http_get(url, opts) do
runner = Application.get_env(:microwaveprop, :gefs_http_get, &Req.get/2)
runner.(url, opts)
end
defp fetch_idx(url) do defp fetch_idx(url) do
case Req.get(url, req_options()) do case http_get(url, req_options()) do
{:ok, %{status: 200, body: body}} -> {:ok, body} {:ok, %{status: 200, body: body}} -> {:ok, body}
{:ok, %{status: status}} -> {:error, "GEFS idx HTTP #{status}"} {:ok, %{status: status}} -> {:error, "GEFS idx HTTP #{status}"}
{:error, reason} -> {:error, reason} {:error, reason} -> {:error, reason}
@ -265,7 +270,7 @@ defmodule Microwaveprop.Weather.GefsClient do
|> Task.Supervisor.async_stream_nolink( |> Task.Supervisor.async_stream_nolink(
merged, merged,
fn {start, stop} -> fn {start, stop} ->
case Req.get(url, [{:headers, [{"Range", "bytes=#{start}-#{stop}"}]} | req_options()]) do case http_get(url, [{:headers, [{"Range", "bytes=#{start}-#{stop}"}]} | req_options()]) do
{:ok, %{status: 206, body: body}} -> {:ok, start, body} {:ok, %{status: 206, body: body}} -> {:ok, start, body}
{:ok, %{status: status}} -> {:error, "GEFS grib HTTP #{status}"} {:ok, %{status: status}} -> {:error, "GEFS grib HTTP #{status}"}
{:error, reason} -> {:error, reason} {:error, reason} -> {:error, reason}

View file

@ -475,13 +475,18 @@ defmodule Microwaveprop.Weather.HrrrClient do
end end
defp do_fetch_idx(url) do defp do_fetch_idx(url) do
case Req.get(url, req_options()) do case http_get(url, req_options()) do
{:ok, %{status: 200, body: body}} -> {:ok, body} {:ok, %{status: 200, body: body}} -> {:ok, body}
{:ok, %{status: status}} -> {:error, "HRRR idx HTTP #{status}"} {:ok, %{status: status}} -> {:error, "HRRR idx HTTP #{status}"}
{:error, reason} -> {:error, reason} {:error, reason} -> {:error, reason}
end end
end end
defp http_get(url, opts) do
runner = Application.get_env(:microwaveprop, :hrrr_http_get, &Req.get/2)
runner.(url, opts)
end
@doc """ @doc """
Downloads the given byte ranges from a GRIB2 URL and concatenates Downloads the given byte ranges from a GRIB2 URL and concatenates
them in offset order. Merges adjacent/overlapping ranges and runs them in offset order. Merges adjacent/overlapping ranges and runs
@ -583,7 +588,7 @@ defmodule Microwaveprop.Weather.HrrrClient do
|> Task.Supervisor.async_stream_nolink( |> Task.Supervisor.async_stream_nolink(
merged, merged,
fn {start, stop} -> fn {start, stop} ->
case Req.get(url, [{:headers, [{"Range", "bytes=#{start}-#{stop}"}]} | req_options()]) do case http_get(url, [{:headers, [{"Range", "bytes=#{start}-#{stop}"}]} | req_options()]) do
{:ok, %{status: 206, body: body}} -> {:ok, start, body} {:ok, %{status: 206, body: body}} -> {:ok, start, body}
{:ok, %{status: status}} -> {:error, "HRRR grib HTTP #{status}"} {:ok, %{status: status}} -> {:error, "HRRR grib HTTP #{status}"}
{:error, reason} -> {:error, reason} {:error, reason} -> {:error, reason}

View file

@ -352,4 +352,53 @@ defmodule Microwaveprop.Weather.GefsClientTest do
end end
end end
end end
describe "dewpoint_from_rh/2 edge cases" do
test "handles 0% RH by clamping to a small positive value" do
result = GefsClient.dewpoint_from_rh(25.0, 0.0)
assert is_float(result)
assert result < 25.0
end
test "100% RH at 25°C returns ~25°C dewpoint" do
result = GefsClient.dewpoint_from_rh(25.0, 100.0)
assert_in_delta result, 25.0, 0.5
end
test "50% RH at 25°C returns dewpoint below 25°C" do
result = GefsClient.dewpoint_from_rh(25.0, 50.0)
assert result < 25.0
assert result > 10.0
end
end
describe "fetch_grid_profiles/3 with injected http_get" do
setup do
prev = Application.get_env(:microwaveprop, :gefs_http_get)
on_exit(fn ->
if prev,
do: Application.put_env(:microwaveprop, :gefs_http_get, prev),
else: Application.delete_env(:microwaveprop, :gefs_http_get)
end)
:ok
end
test "returns error when idx fetch fails" do
Application.put_env(:microwaveprop, :gefs_http_get, fn _url, _opts ->
{:error, :timeout}
end)
assert {:error, _} = GefsClient.fetch_grid_profiles(~D[2026-04-18], 12, 0)
end
test "returns error when idx returns non-200" do
Application.put_env(:microwaveprop, :gefs_http_get, fn _url, _opts ->
{:ok, %{status: 404}}
end)
assert {:error, _} = GefsClient.fetch_grid_profiles(~D[2026-04-18], 12, 0)
end
end
end end