prop/test/microwaveprop/weather/grid_cache_valkey_test.exs
Graham McIntire 079346a1b9
Some checks failed
Build and Push / Build and Push Docker Image (push) Failing after 4m38s
fix: resolve all 281 credo issues across source and test files
- F-level (228): replace length/1 with Enum.count_until/2 or pattern
  matching; convert Enum.flat_map+if to Enum.filter+Enum.map; fix
  identity case in narr_client
- W-level (46): normalize dual atom/string key access in weather_layers,
  beacon_measurements, surface, skewt_live, contact_live/show; add
  :data_provider and weather-map assigns to ignored_assigns in credo
  config (consumed by child components credo can't trace); remove
  weak is_list assertion; remove explicit assert_receive timeout
- R-level (6): replace 'This module provides...' moduledocs with
  meaningful descriptions
- Also fix 4 compile-connected xref issues by deferring
  BandConfig.band_options() from module attribute to runtime

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 09:04:21 -05:00

196 lines
6.5 KiB
Elixir

defmodule Microwaveprop.Weather.GridCacheValkey.EmptyCacheTest do
use ExUnit.Case, async: false
alias Microwaveprop.Valkey.Conn
alias Microwaveprop.Weather.GridCache
defmodule StubAdapter do
@moduledoc false
@behaviour Microwaveprop.Valkey.Adapter
@impl true
def command(_conn, ["ZREVRANGE", "prop:wg:vts", "0", "0"], _opts), do: {:ok, []}
def command(_conn, ["PING"], _opts), do: {:ok, "PONG"}
def command(_conn, ["ZRANGEBYSCORE", "prop:wg:vts", "-inf", _], _opts), do: {:ok, []}
def command(_conn, ["GET" | _], _opts), do: {:ok, nil}
def command(_conn, ["MGET" | _], _opts), do: {:ok, [nil]}
def command(_conn, ["SCAN", "0", "MATCH", _, "COUNT", "500"], _opts), do: {:ok, ["0", []]}
def command(_conn, _, _opts), do: {:ok, ["0", []]}
@impl true
def pipeline(_conn, cmds, _opts), do: {:ok, List.duplicate("OK", length(cmds))}
end
setup do
prev_url = Application.get_env(:microwaveprop, :valkey_url)
prev_adapter = Application.get_env(:microwaveprop, :valkey_adapter)
Application.put_env(:microwaveprop, :valkey_url, "redis://localhost:6379")
Application.put_env(:microwaveprop, :valkey_adapter, StubAdapter)
case Process.whereis(Conn) do
nil -> :ok
_ -> Process.unregister(Conn)
end
{:ok, pid} = Task.start(fn -> Process.sleep(:infinity) end)
Process.register(pid, Conn)
GridCache.clear()
on_exit(fn ->
if pid && Process.alive?(pid) do
Process.unregister(Conn)
Process.exit(pid, :kill)
end
if prev_url,
do: Application.put_env(:microwaveprop, :valkey_url, prev_url),
else: Application.delete_env(:microwaveprop, :valkey_url)
if prev_adapter,
do: Application.put_env(:microwaveprop, :valkey_adapter, prev_adapter),
else: Application.delete_env(:microwaveprop, :valkey_adapter)
end)
:ok
end
test "fetch returns :miss when ZREVRANGE returns empty" do
assert GridCache.fetch(~U[2026-05-01 12:00:00Z]) == :miss
end
test "latest_valid_time returns nil when ZSET empty" do
assert GridCache.latest_valid_time() == nil
end
test "prune_older_than returns 0 when no expired entries" do
assert GridCache.prune_older_than(~U[2026-05-01 00:00:00Z]) == 0
end
test "broadcast_put writes to valkey" do
assert GridCache.broadcast_put(~U[2026-05-01 12:00:00Z], [%{lat: 32.0, lon: -97.0, temperature: 25.0}]) == :ok
end
test "put writes chunks and updates index" do
assert GridCache.put(~U[2026-05-01 12:00:00Z], [%{lat: 32.0, lon: -97.0, temperature: 25.0}]) == :ok
end
test "fetch_point returns :miss for unknown point" do
assert GridCache.fetch_point(~U[2026-05-01 12:00:00Z], 32.0, -97.0) == :miss
end
test "fetch_bounds returns :miss for empty cache" do
assert GridCache.fetch_bounds(~U[2026-05-01 12:00:00Z], %{"south" => 30, "north" => 35, "west" => -100, "east" => -95}) ==
:miss
end
test "claim_fill and release_fill work" do
vt = ~U[2026-05-01 12:00:00Z]
assert GridCache.claim_fill(vt) == true
assert GridCache.claim_fill(vt) == false
GridCache.release_fill(vt)
assert GridCache.claim_fill(vt) == true
end
end
defmodule Microwaveprop.Weather.GridCacheValkey.DataCacheTest do
use ExUnit.Case, async: false
alias Microwaveprop.Valkey.Conn
alias Microwaveprop.Weather.GridCache
defmodule DataStubAdapter do
@moduledoc false
@behaviour Microwaveprop.Valkey.Adapter
@vt_iso "2026-05-01T12:00:00Z"
@chunk_key "prop:wg:#{@vt_iso}:6:-20"
@chunk_cells %{
{32.0, -97.0} => %{lat: 32.0, lon: -97.0, temperature: 25.0},
{40.0, -74.0} => %{lat: 40.0, lon: -74.0, temperature: 20.0}
}
@chunk_data %{{6, -20} => @chunk_cells}
# For fetch_point, the code does Map.get(chunk_map, {lat, lon})
# which looks up directly in the outer map. Precompute what the
# actual lookup would return so we can stub correctly.
@encoded :erlang.term_to_binary(@chunk_data)
@impl true
def command(_conn, ["ZREVRANGE", "prop:wg:vts", "0", "0"], _opts), do: {:ok, [@vt_iso]}
def command(_conn, ["SCAN", "0", "MATCH", "prop:wg:#{@vt_iso}:*", "COUNT", "500"], _opts),
do: {:ok, ["0", [@chunk_key]]}
def command(_conn, ["MGET" | tail], _opts) do
vals =
Enum.map(tail, fn
@chunk_key -> @encoded
_ -> nil
end)
{:ok, vals}
end
def command(_conn, ["GET", @chunk_key], _opts), do: {:ok, @encoded}
def command(_conn, ["GET" | _], _opts), do: {:ok, nil}
def command(_conn, ["ZRANGEBYSCORE", "prop:wg:vts", "-inf", _], _opts), do: {:ok, []}
def command(_conn, _, _opts), do: {:ok, ["0", []]}
@impl true
def pipeline(_conn, cmds, _opts), do: {:ok, List.duplicate("OK", length(cmds))}
end
setup do
prev_url = Application.get_env(:microwaveprop, :valkey_url)
prev_adapter = Application.get_env(:microwaveprop, :valkey_adapter)
Application.put_env(:microwaveprop, :valkey_url, "redis://localhost:6379")
Application.put_env(:microwaveprop, :valkey_adapter, DataStubAdapter)
case Process.whereis(Conn) do
nil -> :ok
_ -> Process.unregister(Conn)
end
{:ok, pid} = Task.start(fn -> Process.sleep(:infinity) end)
Process.register(pid, Conn)
GridCache.clear()
on_exit(fn ->
if pid && Process.alive?(pid) do
Process.unregister(Conn)
Process.exit(pid, :kill)
end
if prev_url,
do: Application.put_env(:microwaveprop, :valkey_url, prev_url),
else: Application.delete_env(:microwaveprop, :valkey_url)
if prev_adapter,
do: Application.put_env(:microwaveprop, :valkey_adapter, prev_adapter),
else: Application.delete_env(:microwaveprop, :valkey_adapter)
end)
:ok
end
test "fetch returns decoded rows" do
result = GridCache.fetch(~U[2026-05-01 12:00:00Z])
assert {:ok, rows} = result
assert Enum.count_until(rows, 3) == 2
assert hd(rows).lat == 32.0
assert hd(rows).temperature == 25.0
end
test "fetch_bounds filters rows within bounds" do
bounds = %{"south" => 30.0, "north" => 35.0, "west" => -100.0, "east" => -95.0}
assert {:ok, rows} = GridCache.fetch_bounds(~U[2026-05-01 12:00:00Z], bounds)
assert Enum.count_until(rows, 2) == 1
assert hd(rows).lat == 32.0
end
test "fetch_point returns row for known point" do
assert {:ok, row} = GridCache.fetch_point(~U[2026-05-01 12:00:00Z], 32.0, -97.0)
assert row.temperature == 25.0
end
test "latest_valid_time returns the most recent" do
assert GridCache.latest_valid_time() == ~U[2026-05-01 12:00:00Z]
end
end