From f7a331b708ac30cc12342be3b8e007aca2c6961a Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 24 Apr 2026 08:04:16 -0500 Subject: [PATCH] test: Aprsme.Is reconnect success + connect-failure paths --- test/aprsme/is_test.exs | 76 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/test/aprsme/is_test.exs b/test/aprsme/is_test.exs index 49ef36e..1b510c8 100644 --- a/test/aprsme/is_test.exs +++ b/test/aprsme/is_test.exs @@ -591,6 +591,82 @@ defmodule Aprsme.IsTest do end end + describe "handle_info(:reconnect) with mocked connection" do + # Start a listener, temporarily swap APRS-IS config to point at it, + # then trigger :reconnect so Aprsme.Is opens a real TCP connection. + setup do + {:ok, listen} = :gen_tcp.listen(0, [:binary, active: false, reuseaddr: true]) + {:ok, port} = :inet.port(listen) + + # Spawn an acceptor that reads the login string and closes. + spawn_link(fn -> + case :gen_tcp.accept(listen, 5_000) do + {:ok, srv} -> + _ = :gen_tcp.recv(srv, 0, 1_000) + _ = :gen_tcp.close(srv) + + _ -> + :ok + end + end) + + original_disabled = Application.get_env(:aprsme, :disable_aprs_connection, false) + original_env = Application.get_env(:aprsme, :env) + + Application.put_env(:aprsme, :disable_aprs_connection, false) + Application.put_env(:aprsme, :env, :dev) + + on_exit(fn -> + Application.put_env(:aprsme, :disable_aprs_connection, original_disabled) + Application.put_env(:aprsme, :env, original_env) + _ = :gen_tcp.close(listen) + end) + + {:ok, port: port} + end + + test "reconnect succeeds and updates socket/timers", %{port: port} do + state = build_state(%{server: ~c"127.0.0.1", port: port, socket: nil, timer: nil, keepalive_timer: nil}) + + capture_log(fn -> + assert {:noreply, new_state} = Aprsme.Is.handle_info(:reconnect, state) + + assert is_port(new_state.socket) or is_tuple(new_state.socket) + assert is_reference(new_state.timer) + assert is_reference(new_state.keepalive_timer) + refute new_state.backpressure_active + + # Cleanup + if new_state.socket, do: :gen_tcp.close(new_state.socket) + if new_state.timer, do: Process.cancel_timer(new_state.timer) + if new_state.keepalive_timer, do: Process.cancel_timer(new_state.keepalive_timer) + end) + end + end + + describe "handle_info(:reconnect) when connect fails" do + test "schedules another reconnect attempt on connection error" do + # Port 1 should always refuse or fail — triggers the connect-error branch. + original_disabled = Application.get_env(:aprsme, :disable_aprs_connection, false) + original_env = Application.get_env(:aprsme, :env) + + Application.put_env(:aprsme, :disable_aprs_connection, false) + Application.put_env(:aprsme, :env, :dev) + + on_exit(fn -> + Application.put_env(:aprsme, :disable_aprs_connection, original_disabled) + Application.put_env(:aprsme, :env, original_env) + end) + + state = build_state(%{server: ~c"127.0.0.1", port: 1, socket: nil, timer: nil, keepalive_timer: nil}) + + capture_log(fn -> + assert {:noreply, same_state} = Aprsme.Is.handle_info(:reconnect, state) + assert same_state == state + end) + end + end + describe "handle_info({:backpressure, _}) with active socket" do setup do {client, _listen, cleanup} = loopback_socket()