- Pskr.Client: 0% -> 18% (init/standby/handle_info/terminate) - RoverPlanningLive.PathShow: 0% -> 82% (mount/load_path) - Rover.CandidateDetail: 0% -> partial (summarize/5 with stations) - Mix.Tasks.Weather.RebatchAsos: 0% -> covered (wrapper run/1) - HrrrNativeClient: 57% -> 67% (fetch_native_duct_grid error paths) - RoverLive: 40% -> 48% (toggle/delete/update_station_grid logged-in) - PathLive: 62% -> 63% (path_forecast_detail + rover_path_id branches) - PathCompute: 64% -> 64% (compute/4 full pipeline + resolve_location) - SkewtLive: 29% -> 31% (search/select_time event no-ops) - SkewtLocationResolver: 38% -> 60%+ (callsign cache + error paths) 3557 tests passing.
113 lines
3.3 KiB
Elixir
113 lines
3.3 KiB
Elixir
defmodule Microwaveprop.Pskr.ClientTest do
|
|
use ExUnit.Case, async: false
|
|
|
|
alias Microwaveprop.Pskr.Client
|
|
|
|
setup do
|
|
# Pre-register a placeholder under the global name so the Client
|
|
# under test loses leader election and goes to :standby — this
|
|
# avoids any outbound MQTT connection in the test environment.
|
|
{:ok, placeholder} = Task.start_link(fn -> Process.sleep(:infinity) end)
|
|
:global.unregister_name(Client)
|
|
:yes = :global.register_name(Client, placeholder)
|
|
|
|
on_exit(fn ->
|
|
:global.unregister_name(Client)
|
|
Process.exit(placeholder, :kill)
|
|
end)
|
|
|
|
%{placeholder: placeholder}
|
|
end
|
|
|
|
describe "start_link + init (standby branch)" do
|
|
test "starts as :standby when the global name is already taken" do
|
|
{:ok, pid} = GenServer.start_link(Client, name: __MODULE__.NotRegistered)
|
|
state = :sys.get_state(pid)
|
|
|
|
assert state.role == :standby
|
|
assert state.socket == nil
|
|
assert state.buffer == <<>>
|
|
assert is_list(state.bands)
|
|
assert is_binary(state.client_id)
|
|
|
|
GenServer.stop(pid)
|
|
end
|
|
|
|
test "uses defaults for bands when none are passed" do
|
|
{:ok, pid} = GenServer.start_link(Client, [])
|
|
state = :sys.get_state(pid)
|
|
|
|
assert "6m" in state.bands
|
|
assert "2m" in state.bands
|
|
assert "70cm" in state.bands
|
|
|
|
GenServer.stop(pid)
|
|
end
|
|
|
|
test "honors custom bands list and aggregator name from opts" do
|
|
{:ok, pid} = GenServer.start_link(Client, bands: ["2m"], aggregator: SomeOtherAggregator)
|
|
state = :sys.get_state(pid)
|
|
|
|
assert state.bands == ["2m"]
|
|
assert state.aggregator == SomeOtherAggregator
|
|
|
|
GenServer.stop(pid)
|
|
end
|
|
|
|
test "honors custom client_id" do
|
|
{:ok, pid} = GenServer.start_link(Client, client_id: "test-client-42")
|
|
state = :sys.get_state(pid)
|
|
assert state.client_id == "test-client-42"
|
|
GenServer.stop(pid)
|
|
end
|
|
end
|
|
|
|
describe "terminate/2" do
|
|
test "is graceful when state has no socket" do
|
|
{:ok, pid} = GenServer.start_link(Client, [])
|
|
# Just normal stop — terminate(_, %{socket: nil_or_other})
|
|
GenServer.stop(pid)
|
|
refute Process.alive?(pid)
|
|
end
|
|
end
|
|
|
|
describe "standby handle_info" do
|
|
test "nodeup re-runs election (stays standby while name is taken)" do
|
|
{:ok, pid} = GenServer.start_link(Client, [])
|
|
send(pid, {:nodeup, :test@nowhere})
|
|
Process.sleep(50)
|
|
|
|
state = :sys.get_state(pid)
|
|
assert state.role == :standby
|
|
GenServer.stop(pid)
|
|
end
|
|
|
|
test "nodedown re-runs election (stays standby while name is taken)" do
|
|
{:ok, pid} = GenServer.start_link(Client, [])
|
|
send(pid, {:nodedown, :test@nowhere})
|
|
Process.sleep(50)
|
|
|
|
state = :sys.get_state(pid)
|
|
assert state.role == :standby
|
|
GenServer.stop(pid)
|
|
end
|
|
|
|
test "unrelated info messages are ignored" do
|
|
{:ok, pid} = GenServer.start_link(Client, [])
|
|
send(pid, :unrelated_message)
|
|
Process.sleep(20)
|
|
|
|
assert Process.alive?(pid)
|
|
GenServer.stop(pid)
|
|
end
|
|
|
|
test ":ping with no socket is a no-op" do
|
|
{:ok, pid} = GenServer.start_link(Client, [])
|
|
send(pid, :ping)
|
|
Process.sleep(20)
|
|
state = :sys.get_state(pid)
|
|
assert state.socket == nil
|
|
GenServer.stop(pid)
|
|
end
|
|
end
|
|
end
|