test: expand Is coverage — terminate w/ socket, backpressure idempotency, status fallback

This commit is contained in:
Graham McIntire 2026-04-24 09:47:52 -05:00
parent 4096dbf82d
commit 2a69499488
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59

View file

@ -489,6 +489,32 @@ defmodule Aprsme.IsTest do
assert Process.cancel_timer(timer) == false
assert Process.cancel_timer(keepalive) == false
end
test "closes an active socket on terminate" do
{client, listen, cleanup} = loopback_socket()
state = build_state(%{socket: client, buffer: ""})
capture_log(fn ->
assert Aprsme.Is.terminate(:normal, state) == :normal
end)
# Socket should be closed after terminate
assert :gen_tcp.send(client, "X") == {:error, :closed}
:gen_tcp.close(listen)
cleanup.()
end
test "cancels safety_valve_timer on terminate" do
safety_timer = Process.send_after(self(), :safety, to_timeout(minute: 5))
state = build_state(%{socket: nil, safety_valve_timer: safety_timer})
capture_log(fn ->
assert Aprsme.Is.terminate(:normal, state) == :normal
end)
assert Process.cancel_timer(safety_timer) == false
end
end
describe "code_change/3" do
@ -787,6 +813,27 @@ defmodule Aprsme.IsTest do
Process.cancel_timer(new_state.timer)
end)
end
test "activate is a no-op when backpressure is already active", %{socket: socket} do
state = build_state(%{socket: socket, backpressure_active: true})
assert {:noreply, ^state} =
Aprsme.Is.handle_info({:backpressure, :activate}, state)
end
test "activate is a no-op when there is no socket" do
state = build_state(%{socket: nil, backpressure_active: false})
assert {:noreply, ^state} =
Aprsme.Is.handle_info({:backpressure, :activate}, state)
end
test "deactivate is a no-op when backpressure is already inactive" do
state = build_state(%{socket: nil, backpressure_active: false})
assert {:noreply, ^state} =
Aprsme.Is.handle_info({:backpressure, :deactivate}, state)
end
end
describe "send_message/3 and /1 helpers" do
@ -1224,4 +1271,49 @@ defmodule Aprsme.IsTest do
end)
end
end
describe "get_status/0 with a running process under the module name" do
test "falls back to the disconnected status when the GenServer doesn't respond" do
# Register a minimal process under the Aprsme.Is name that doesn't know
# how to handle :get_status. GenServer.call/3 to a non-GenServer raises
# :exit — status_from/1 catches it and returns disconnected_status with
# the rotate.aprs2.net fallback.
parent = self()
pid =
spawn(fn ->
receive do
:stop -> :ok
after
5_000 -> :ok
end
send(parent, :done)
end)
# Unregister any prior registration first.
if Process.whereis(Aprsme.Is) do
:erlang.unregister(Aprsme.Is)
end
true = Process.register(pid, Aprsme.Is)
try do
# Trap exits so the :exit from GenServer.call doesn't kill us.
Process.flag(:trap_exit, true)
# capture_log because disconnected_status logs nothing, but be defensive.
status = Aprsme.Is.get_status()
assert status.connected == false
assert is_map(status)
after
if Process.whereis(Aprsme.Is) == pid do
:erlang.unregister(Aprsme.Is)
end
send(pid, :stop)
end
end
end
end