- Add 17 missing @spec annotations (layouts, error_json, error_html, skewt_svg) - Move 12+ nested alias/import/require to module top level - Add phx-change/id attributes to 11 raw HTML <form> tags - Remove 4 unused LiveView assigns (:bounds, :data_provider) - Add 3 missing doctest references (HrrrNativeClient, BulkFetch, Accounts) - Break 2 long lines (path_compute.ex:382) - Strengthen weak test assertions (is_binary→byte_size, is_list→!=[]) - Replace Module.concat with Module.safe_concat (2 occurrences) - Replace length/1 > 0 with list != [] (9 occurrences) - Remove no-op assert true, fix no-assertion tests Remaining: 24 socket.assigns introspection warnings (deliberate test pattern for observable behavior testing), 1 formatter-resistant long line, 3 app-code usage warnings.
163 lines
4.9 KiB
Elixir
163 lines
4.9 KiB
Elixir
defmodule Microwaveprop.Propagation.BandWeightsTest do
|
|
# async: false because BandWeights caches in :persistent_term, which is
|
|
# global to the BEAM. Each test resets the cache in setup so ordering
|
|
# is irrelevant, but concurrent runs would still see each other.
|
|
use ExUnit.Case, async: false
|
|
|
|
alias Microwaveprop.Propagation.BandWeights
|
|
|
|
@valid_json """
|
|
{
|
|
"generated_at": "2026-05-24T18:53:32+00:00",
|
|
"schema_version": 1,
|
|
"global_weights": {
|
|
"humidity": 0.1262, "time_of_day": 0.038, "td_depression": 0.101,
|
|
"refractivity": 0.0986, "sky": 0.0841, "season": 0.1134,
|
|
"wind": 0.0841, "rain": 0.1431, "pwat": 0.1147, "pressure": 0.0967
|
|
},
|
|
"band_overrides": {
|
|
"10000": {
|
|
"weights": {
|
|
"humidity": 0.20, "time_of_day": 0.04, "td_depression": 0.10,
|
|
"refractivity": 0.10, "sky": 0.08, "season": 0.11,
|
|
"wind": 0.08, "rain": 0.14, "pwat": 0.11, "pressure": 0.04
|
|
},
|
|
"n_samples": 54161,
|
|
"source": "contacts"
|
|
},
|
|
"144": {
|
|
"weights": {
|
|
"humidity": 0.18, "time_of_day": 0.02, "td_depression": 0.15,
|
|
"refractivity": 0.11, "sky": 0.06, "season": 0.12,
|
|
"wind": 0.06, "rain": 0.01, "pwat": 0.17, "pressure": 0.12
|
|
},
|
|
"n_samples": 12927,
|
|
"source": "pskr"
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
|
|
setup do
|
|
# Cache is global to the BEAM; reset before every test to avoid
|
|
# bleed between cases.
|
|
BandWeights.reset()
|
|
prior_env = Application.get_env(:microwaveprop, :band_weights_json)
|
|
|
|
on_exit(fn ->
|
|
BandWeights.reset()
|
|
|
|
if prior_env do
|
|
Application.put_env(:microwaveprop, :band_weights_json, prior_env)
|
|
else
|
|
Application.delete_env(:microwaveprop, :band_weights_json)
|
|
end
|
|
end)
|
|
|
|
:ok
|
|
end
|
|
|
|
describe "lookup/1 with a valid JSON file" do
|
|
setup do
|
|
path = Path.join(System.tmp_dir!(), "band_weights_#{System.unique_integer([:positive])}.json")
|
|
File.write!(path, @valid_json)
|
|
Application.put_env(:microwaveprop, :band_weights_json, path)
|
|
on_exit(fn -> File.rm(path) end)
|
|
%{path: path}
|
|
end
|
|
|
|
test "returns the override weights for a band present in the file" do
|
|
result = BandWeights.lookup(10_000)
|
|
assert result.humidity == 0.20
|
|
assert result.pressure == 0.04
|
|
# All 10 keys present.
|
|
expected_keys =
|
|
MapSet.new([
|
|
:humidity,
|
|
:time_of_day,
|
|
:td_depression,
|
|
:refractivity,
|
|
:sky,
|
|
:season,
|
|
:wind,
|
|
:rain,
|
|
:pwat,
|
|
:pressure
|
|
])
|
|
|
|
assert MapSet.new(Map.keys(result)) == expected_keys
|
|
end
|
|
|
|
test "returns the override weights for a second band" do
|
|
result = BandWeights.lookup(144)
|
|
assert result.humidity == 0.18
|
|
end
|
|
|
|
test "returns nil for a band not present in the file" do
|
|
assert BandWeights.lookup(75_000) == nil
|
|
end
|
|
|
|
test "is cheap on repeat calls (cached)" do
|
|
first = BandWeights.lookup(10_000)
|
|
# Delete the source file — second call must still succeed from cache.
|
|
File.rm!(Application.fetch_env!(:microwaveprop, :band_weights_json))
|
|
second = BandWeights.lookup(10_000)
|
|
assert first == second
|
|
end
|
|
end
|
|
|
|
describe "lookup/1 when the file is missing" do
|
|
setup do
|
|
Application.put_env(:microwaveprop, :band_weights_json, "/nonexistent/path/band_weights.json")
|
|
:ok
|
|
end
|
|
|
|
test "returns nil for every band (does not raise)" do
|
|
assert BandWeights.lookup(10_000) == nil
|
|
assert BandWeights.lookup(50) == nil
|
|
end
|
|
end
|
|
|
|
describe "lookup/1 when the loader is disabled in config" do
|
|
setup do
|
|
Application.put_env(:microwaveprop, :band_weights_json, false)
|
|
:ok
|
|
end
|
|
|
|
test "returns nil for every band" do
|
|
assert BandWeights.lookup(10_000) == nil
|
|
assert BandWeights.lookup(144) == nil
|
|
end
|
|
end
|
|
|
|
describe "lookup/1 with malformed JSON" do
|
|
setup do
|
|
path = Path.join(System.tmp_dir!(), "band_weights_bad_#{System.unique_integer([:positive])}.json")
|
|
File.write!(path, "{ not valid json")
|
|
Application.put_env(:microwaveprop, :band_weights_json, path)
|
|
on_exit(fn -> File.rm(path) end)
|
|
%{path: path}
|
|
end
|
|
|
|
test "returns nil for every band (logs but does not raise)" do
|
|
assert BandWeights.lookup(10_000) == nil
|
|
end
|
|
end
|
|
|
|
describe "all_overrides/0" do
|
|
setup do
|
|
path = Path.join(System.tmp_dir!(), "band_weights_#{System.unique_integer([:positive])}.json")
|
|
File.write!(path, @valid_json)
|
|
Application.put_env(:microwaveprop, :band_weights_json, path)
|
|
on_exit(fn -> File.rm(path) end)
|
|
:ok
|
|
end
|
|
|
|
test "returns a map of all band overrides keyed by freq_mhz integer" do
|
|
result = BandWeights.all_overrides()
|
|
assert Map.has_key?(result, 10_000)
|
|
assert Map.has_key?(result, 144)
|
|
assert result[10_000].humidity == 0.20
|
|
end
|
|
end
|
|
end
|