test: format all test files + GridCache Valkey backend (49%->74%)

Coverage: 79.09% -> 79.30% (GridCache 49% -> 74%)
This commit is contained in:
Graham McIntire 2026-05-07 16:04:32 -05:00
parent 6028e8a838
commit 5d90d52a45
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
12 changed files with 184 additions and 54 deletions

View file

@ -39,7 +39,7 @@ defmodule Microwaveprop.Buildings.IndexTest do
records = Index.records_near(32.8000, -97.0000, 50)
assert length(records) == 2
heights = Enum.map(records, & &1.height_m) |> Enum.sort()
heights = records |> Enum.map(& &1.height_m) |> Enum.sort()
assert heights == [8.0, 25.0]
end

View file

@ -13,8 +13,10 @@ defmodule Microwaveprop.Buildings.LoaderTest do
on_exit(fn ->
File.rm_rf(tmp)
if prev, do: Application.put_env(:microwaveprop, :buildings_cache_dir, prev),
else: Application.delete_env(:microwaveprop, :buildings_cache_dir)
if prev,
do: Application.put_env(:microwaveprop, :buildings_cache_dir, prev),
else: Application.delete_env(:microwaveprop, :buildings_cache_dir)
end)
:ok
@ -37,8 +39,10 @@ defmodule Microwaveprop.Buildings.LoaderTest do
File.mkdir_p!(cache_dir)
geojsonl =
~s|{"type": "Feature", "properties": {"height": 5.5}, "geometry": {"type": "Polygon", "coordinates": [[[-97.736, 32.221], [-97.736, 32.222], [-97.735, 32.222], [-97.735, 32.221], [-97.736, 32.221]]]}}|
|> then(&:zlib.gzip/1)
then(
~s|{"type": "Feature", "properties": {"height": 5.5}, "geometry": {"type": "Polygon", "coordinates": [[[-97.736, 32.221], [-97.736, 32.222], [-97.735, 32.222], [-97.735, 32.221], [-97.736, 32.221]]]}}|,
&:zlib.gzip/1
)
File.write!(Path.join(cache_dir, "#{quadkey}.csv.gz"), geojsonl)

View file

@ -287,10 +287,13 @@ defmodule Microwaveprop.Commercial.SnmpClientTest do
describe "poll/3 with injected snmp_runner" do
setup do
prev = Application.get_env(:microwaveprop, :snmp_runner)
on_exit(fn ->
if prev, do: Application.put_env(:microwaveprop, :snmp_runner, prev),
else: Application.delete_env(:microwaveprop, :snmp_runner)
if prev,
do: Application.put_env(:microwaveprop, :snmp_runner, prev),
else: Application.delete_env(:microwaveprop, :snmp_runner)
end)
:ok
end

View file

@ -67,8 +67,10 @@ defmodule Microwaveprop.Propagation.PathComputeTest do
describe "compute_loss_budget/5 property" do
property "total is always >= fspl" do
check all dist_km <- float(min: 0.1, max: 500),
band_config = BandConfig.get(10_000) do
check all(
dist_km <- float(min: 0.1, max: 500),
band_config = BandConfig.get(10_000)
) do
result = PathCompute.compute_loss_budget(dist_km, 10.0, band_config, nil, nil)
assert result.total >= result.fspl
assert result.total > 0
@ -78,8 +80,10 @@ defmodule Microwaveprop.Propagation.PathComputeTest do
property "all loss components are non-negative across known bands" do
bands = [50, 144, 222, 432, 902, 1_296, 2_304, 3_400, 5_760, 10_000, 24_000, 47_000, 75_000]
check all dist_km <- float(min: 0.1, max: 500),
band_mhz <- member_of(bands) do
check all(
dist_km <- float(min: 0.1, max: 500),
band_mhz <- member_of(bands)
) do
band_config = BandConfig.get(band_mhz)
freq_ghz = band_mhz / 1000
result = PathCompute.compute_loss_budget(dist_km, freq_ghz, band_config, nil, nil)

View file

@ -239,8 +239,8 @@ defmodule Microwaveprop.Rover.ComputeTest do
cell_scores = Map.new(result.cells, fn c -> {{c.lat, c.lon}, c.score} end)
penalized_score = cell_scores[cell_with_penalty]
other_scores = Map.drop(cell_scores, [cell_with_penalty]) |> Map.values()
assert penalized_score != nil
other_scores = cell_scores |> Map.delete(cell_with_penalty) |> Map.values()
assert penalized_score
assert Enum.all?(other_scores, fn s -> s > penalized_score end)
after
Application.put_env(:microwaveprop, :rover_road_proximity_enabled, true)

View file

@ -72,7 +72,7 @@ defmodule Microwaveprop.Rover.ElevationTest do
test "returns nil for SRTM void sentinel values (-32768)" do
tile_path = Path.join(Application.get_env(:microwaveprop, :srtm_tiles_dir), "N00E000.hgt")
# SRTM void sentinel is -32768
File.write!(tile_path, <<(-32_768)::signed-big-integer-size(16)>>)
File.write!(tile_path, <<-32_768::signed-big-integer-size(16)>>)
result = Elevation.lookup_many([{0.9999, 0.0}])
assert result[{0.9999, 0.0}] == nil

View file

@ -6,6 +6,7 @@ defmodule Microwaveprop.Rover.ProminenceTest do
describe "prominence_map/2" do
test "returns map keyed by {lat, lon}" do
cells = [%{lat: 33.0, lon: -97.0}, %{lat: 33.1, lon: -97.1}]
elev_lookup = fn points ->
Map.new(points, fn p -> {p, 200} end)
end

View file

@ -240,10 +240,12 @@ defmodule Microwaveprop.Terrain.ViewshedTest do
use ExUnitProperties
property "short bearings round-trip for CONUS-mid latitudes" do
check all lat <- float(min: 25, max: 50),
lon <- float(min: -125, max: -65),
bearing <- float(min: 0, max: 360),
dist_km <- float(min: 0.1, max: 20) do
check all(
lat <- float(min: 25, max: 50),
lon <- float(min: -125, max: -65),
bearing <- float(min: 0, max: 360),
dist_km <- float(min: 0.1, max: 20)
) do
{lat2, lon2} = Viewshed.destination_point(lat, lon, bearing, dist_km)
back_bearing = if(bearing < 180, do: bearing + 180, else: bearing - 180)
{lat3, lon3} = Viewshed.destination_point(lat2, lon2, back_bearing, dist_km)
@ -253,8 +255,10 @@ defmodule Microwaveprop.Terrain.ViewshedTest do
end
property "east-west bearing preserves latitude at equator" do
check all lon <- float(min: -170, max: 170),
dist_km <- float(min: 1, max: 50) do
check all(
lon <- float(min: -170, max: 170),
dist_km <- float(min: 1, max: 50)
) do
{lat2, lon2} = Viewshed.destination_point(0.0, lon, 90, dist_km)
assert_in_delta lat2, 0.0, 0.01
assert lon2 != lon

View file

@ -4,22 +4,24 @@ defmodule Microwaveprop.ValkeyTest do
import Mox
alias Microwaveprop.Valkey
# Register Mox mock as the adapter and fake a Redix connection so
# configured?() returns true, letting command/pipeline run through the mock.
alias Microwaveprop.Valkey.Conn
alias Microwaveprop.Valkey.MockAdapter
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, Microwaveprop.Valkey.MockAdapter)
Application.put_env(:microwaveprop, :valkey_adapter, MockAdapter)
# Start a dummy process registered at the connection name.
{:ok, pid} = Task.start(fn -> Process.sleep(:infinity) end)
Process.register(pid, Microwaveprop.Valkey.Conn)
Process.register(pid, Conn)
on_exit(fn ->
Process.unregister(Microwaveprop.Valkey.Conn)
Process.unregister(Conn)
Process.exit(pid, :kill)
if prev_url,
@ -52,7 +54,7 @@ defmodule Microwaveprop.ValkeyTest do
describe "get/1" do
test "returns :miss when key is nil" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["GET", "missing"], _opts ->
expect(MockAdapter, :command, fn _conn, ["GET", "missing"], _opts ->
{:ok, nil}
end)
@ -60,7 +62,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "returns decoded term on hit" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["GET", "key"], _opts ->
expect(MockAdapter, :command, fn _conn, ["GET", "key"], _opts ->
{:ok, :erlang.term_to_binary(%{foo: "bar"})}
end)
@ -68,7 +70,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "propagates error" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["GET", "key"], _opts ->
expect(MockAdapter, :command, fn _conn, ["GET", "key"], _opts ->
{:error, :connection_lost}
end)
@ -82,7 +84,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "returns decoded values preserving order" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["MGET", "a", "b"], _opts ->
expect(MockAdapter, :command, fn _conn, ["MGET", "a", "b"], _opts ->
{:ok, [nil, :erlang.term_to_binary(42)]}
end)
@ -90,7 +92,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "propagates error" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["MGET", "key"], _opts ->
expect(MockAdapter, :command, fn _conn, ["MGET", "key"], _opts ->
{:error, :connection_lost}
end)
@ -100,7 +102,7 @@ defmodule Microwaveprop.ValkeyTest do
describe "set/3" do
test "returns :ok on success" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["SET", "k", _, "EX", "3600"], _opts ->
expect(MockAdapter, :command, fn _conn, ["SET", "k", _, "EX", "3600"], _opts ->
{:ok, "OK"}
end)
@ -108,7 +110,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "propagates error" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["SET", "k", _, "EX", "3600"], _opts ->
expect(MockAdapter, :command, fn _conn, ["SET", "k", _, "EX", "3600"], _opts ->
{:error, :connection_lost}
end)
@ -116,7 +118,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "handles unexpected response" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["SET", "k", _, "EX", "3600"], _opts ->
expect(MockAdapter, :command, fn _conn, ["SET", "k", _, "EX", "3600"], _opts ->
{:ok, "QUEUED"}
end)
@ -130,9 +132,9 @@ defmodule Microwaveprop.ValkeyTest do
end
test "returns :ok when all SETs succeed" do
expect(Microwaveprop.Valkey.MockAdapter, :pipeline, fn _conn, cmds, _opts ->
expect(MockAdapter, :pipeline, fn _conn, cmds, _opts ->
assert length(cmds) == 2
assert hd(cmds) |> List.first() == "SET"
assert cmds |> hd() |> List.first() == "SET"
{:ok, ["OK", "OK"]}
end)
@ -140,7 +142,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "returns {:error, {:partial, _}} when some SETs fail" do
expect(Microwaveprop.Valkey.MockAdapter, :pipeline, fn _conn, _cmds, _opts ->
expect(MockAdapter, :pipeline, fn _conn, _cmds, _opts ->
{:ok, ["OK", "ERROR"]}
end)
@ -148,7 +150,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "propagates pipeline error" do
expect(Microwaveprop.Valkey.MockAdapter, :pipeline, fn _conn, _cmds, _opts ->
expect(MockAdapter, :pipeline, fn _conn, _cmds, _opts ->
{:error, :connection_lost}
end)
@ -158,7 +160,7 @@ defmodule Microwaveprop.ValkeyTest do
describe "zadd/3" do
test "returns :ok on success" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["ZADD", "zset", "1.5", "mem"], _opts ->
expect(MockAdapter, :command, fn _conn, ["ZADD", "zset", "1.5", "mem"], _opts ->
{:ok, 1}
end)
@ -166,7 +168,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "propagates error" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["ZADD", "zset", "1.0", "mem"], _opts ->
expect(MockAdapter, :command, fn _conn, ["ZADD", "zset", "1.0", "mem"], _opts ->
{:error, :connection_lost}
end)
@ -176,7 +178,7 @@ defmodule Microwaveprop.ValkeyTest do
describe "zrevrange/3" do
test "returns members list" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["ZREVRANGE", "zset", "0", "10"], _opts ->
expect(MockAdapter, :command, fn _conn, ["ZREVRANGE", "zset", "0", "10"], _opts ->
{:ok, ["a", "b"]}
end)
@ -184,7 +186,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "propagates error" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, _, _opts ->
expect(MockAdapter, :command, fn _conn, _, _opts ->
{:error, :connection_lost}
end)
@ -194,9 +196,7 @@ defmodule Microwaveprop.ValkeyTest do
describe "zrangebyscore/3" do
test "returns members list" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn,
["ZRANGEBYSCORE", "zset", "0", "100"],
_opts ->
expect(MockAdapter, :command, fn _conn, ["ZRANGEBYSCORE", "zset", "0", "100"], _opts ->
{:ok, ["a", "b"]}
end)
@ -210,7 +210,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "removes members" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["ZREM", "zset", "a", "b"], _opts ->
expect(MockAdapter, :command, fn _conn, ["ZREM", "zset", "a", "b"], _opts ->
{:ok, 2}
end)
@ -224,7 +224,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "deletes keys" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["DEL", "k1", "k2"], _opts ->
expect(MockAdapter, :command, fn _conn, ["DEL", "k1", "k2"], _opts ->
{:ok, 2}
end)
@ -234,7 +234,7 @@ defmodule Microwaveprop.ValkeyTest do
describe "scan_match/1" do
test "returns all matching keys from single pass" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, ["SCAN", "0", "MATCH", "pfx:*", "COUNT", "500"], _opts ->
expect(MockAdapter, :command, fn _conn, ["SCAN", "0", "MATCH", "pfx:*", "COUNT", "500"], _opts ->
{:ok, ["0", ["pfx:a", "pfx:b"]]}
end)
@ -242,7 +242,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "follows cursor across multiple passes" do
Microwaveprop.Valkey.MockAdapter
MockAdapter
|> expect(:command, fn _conn, ["SCAN", "0", "MATCH", "pfx:*", "COUNT", "500"], _opts ->
{:ok, ["3", ["pfx:a"]]}
end)
@ -254,7 +254,7 @@ defmodule Microwaveprop.ValkeyTest do
end
test "propagates error" do
expect(Microwaveprop.Valkey.MockAdapter, :command, fn _conn, _, _opts ->
expect(MockAdapter, :command, fn _conn, _, _opts ->
{:error, :connection_lost}
end)

View file

@ -385,16 +385,18 @@ defmodule Microwaveprop.Weather.Grib2.Wgrib2Test do
use ExUnitProperties
property "returns nil for random noise strings" do
check all noise <- string(:printable, max_length: 100) do
check all(noise <- string(:printable, max_length: 100)) do
result = Wgrib2.parse_lon_val_segment(noise)
assert is_nil(result) or is_tuple(result)
end
end
property "round-trips lon through the 0..360 wgrib2 convention" do
check all lat <- float(min: -89.999, max: 89.999),
raw_lon <- float(min: -179.999, max: 179.999),
val <- float(min: -1000, max: 10_000) do
check all(
lat <- float(min: -89.999, max: 89.999),
raw_lon <- float(min: -179.999, max: 179.999),
val <- float(min: -1000, max: 10_000)
) do
lat_str = :erlang.float_to_binary(lat, decimals: 3)
wgrib2_lon = if(raw_lon < 0, do: raw_lon + 360, else: raw_lon)
lon_str = :erlang.float_to_binary(wgrib2_lon, decimals: 3)

View file

@ -0,0 +1,112 @@
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

View file

@ -22,8 +22,8 @@ defmodule Microwaveprop.Weather.MapLayersTest do
end
test "all layer ids are unique" do
ids = MapLayers.all() |> Enum.map(& &1.id)
assert length(ids) == Enum.uniq(ids) |> length()
ids = Enum.map(MapLayers.all(), & &1.id)
assert length(ids) == ids |> Enum.uniq() |> length()
end
test "includes temperature as the first layer" do