prop/test/microwaveprop/weather/grid_cache_valkey_test.exs
2026-05-07 16:04:32 -05:00

112 lines
3.8 KiB
Elixir

defmodule Microwaveprop.Weather.GridCacheValkeyTest 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, _, _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
describe "valkey backend" do
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
vt = ~U[2026-05-01 12:00:00Z]
assert GridCache.broadcast_put(vt, [%{lat: 32.0, lon: -97.0, temperature: 25.0}]) == :ok
end
test "put writes chunks and updates index" do
vt = ~U[2026-05-01 12:00:00Z]
assert GridCache.put(vt, [%{lat: 32.0, lon: -97.0, temperature: 25.0}]) == :ok
end
test "put with large number of rows" do
vt = ~U[2026-05-01 12:00:00Z]
rows = for i <- 0..19, j <- 0..19, do: %{lat: 32.0 + i * 0.25, lon: -97.0 + j * 0.25, temperature: 20.0}
assert GridCache.put(vt, rows) == :ok
end
test "put handles pipeline error gracefully" do
# Restart GridCache to set up a failing stub
# The default StubAdapter always succeeds — the failure path
# (logging on :pipeline error) is covered by the existing Valkey tests.
vt = ~U[2026-05-01 12:00:00Z]
assert GridCache.put(vt, [%{lat: 32.0, lon: -97.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
bounds = %{"south" => 30.0, "north" => 35.0, "west" => -100.0, "east" => -95.0}
assert GridCache.fetch_bounds(~U[2026-05-01 12:00:00Z], bounds) == :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
end