aprs.me/test/aprsme/connection_monitor_test.exs
Graham McIntire b86153cd27
Fix all mix credo --strict warnings (188 → 0)
- Replace apply/2 with direct fully-qualified calls in movement_test
- Fix assert_receive timeouts < 1000ms across 8 test files
- Move nested import statements to module-level scope
- Fix tests with no assertions and add missing doctest
- Replace weak type assertions with specific value checks
- Fix conditional assertions and length/1 expensive patterns
- Disable inappropriate Jump.CredoChecks.AvoidSocketAssignsInTest
- Fix tests not calling application code with credo:disable
- Add various credo:disable comments for legitimate patterns
2026-06-12 16:27:20 -05:00

168 lines
5.4 KiB
Elixir

defmodule Aprsme.ConnectionMonitorTest do
use ExUnit.Case, async: false
alias Aprsme.ConnectionMonitor
describe "cluster disabled (default test config)" do
test "init/1 returns :ignore when cluster disabled" do
assert ConnectionMonitor.init([]) == :ignore
end
test "accepting_connections?/0 returns true when cluster disabled" do
assert ConnectionMonitor.accepting_connections?() == true
end
test "get_stats/0 returns default stats when cluster disabled" do
assert ConnectionMonitor.get_stats() == %{connections: 0, cpu: 0.0, memory: 0.0}
end
end
describe "cluster enabled without a running monitor" do
test "get_stats/0 returns default stats instead of crashing" do
Application.put_env(:aprsme, :cluster_enabled, true)
on_exit(fn ->
Application.put_env(:aprsme, :cluster_enabled, false)
end)
assert ConnectionMonitor.get_stats() == %{connections: 0, cpu: 0.0, memory: 0.0}
end
end
describe "cluster enabled" do
setup do
Application.put_env(:aprsme, :cluster_enabled, true)
{:ok, pid} = ConnectionMonitor.start_link([])
on_exit(fn ->
if Process.alive?(pid) do
try do
GenServer.stop(pid)
catch
:exit, _ -> :ok
end
end
Application.put_env(:aprsme, :cluster_enabled, false)
end)
%{pid: pid}
end
test "register_connection/0 increments connections" do
ConnectionMonitor.register_connection()
# Allow the cast to be processed
stats = ConnectionMonitor.get_stats()
assert stats.connections == 1
end
test "unregister_connection/0 decrements connections" do
ConnectionMonitor.register_connection()
ConnectionMonitor.register_connection()
ConnectionMonitor.unregister_connection()
stats = ConnectionMonitor.get_stats()
assert stats.connections == 1
end
test "unregister_connection/0 does not go below 0" do
ConnectionMonitor.unregister_connection()
stats = ConnectionMonitor.get_stats()
assert stats.connections == 0
end
test "accepting_connections?/0 returns true when not draining" do
assert ConnectionMonitor.accepting_connections?() == true
end
test "accepting_connections?/0 returns false when draining", %{pid: pid} do
:sys.replace_state(pid, fn state -> %{state | draining: true} end)
assert ConnectionMonitor.accepting_connections?() == false
end
test ":check_load handler keeps process alive", %{pid: pid} do
send(pid, :check_load)
# Give it time to process
_ = ConnectionMonitor.get_stats()
assert Process.alive?(pid)
end
test "get_stats/0 returns cpu and memory fields", %{pid: _pid} do
stats = ConnectionMonitor.get_stats()
assert Map.has_key?(stats, :cpu)
assert Map.has_key?(stats, :memory)
assert is_number(stats.cpu)
assert is_number(stats.memory)
end
test "draining triggers when connections exceed imbalance ratio", %{pid: pid} do
# Simulate having many connections to trigger draining analysis
for _ <- 1..20 do
ConnectionMonitor.register_connection()
end
# Force a load check which will gather stats and analyze
send(pid, :check_load)
Process.sleep(100)
assert Process.alive?(pid)
stats = ConnectionMonitor.get_stats()
assert stats.connections == 20
end
test "register_connection/0 is a no-op when cluster disabled" do
Application.put_env(:aprsme, :cluster_enabled, false)
# Should not crash even though the GenServer is running
assert ConnectionMonitor.register_connection() == nil
Application.put_env(:aprsme, :cluster_enabled, true)
end
test "unregister_connection/0 is a no-op when cluster disabled" do
Application.put_env(:aprsme, :cluster_enabled, false)
assert ConnectionMonitor.unregister_connection() == nil
Application.put_env(:aprsme, :cluster_enabled, true)
end
test "draining is set to true when state has high connections", %{pid: pid} do
# Set up state with draining true and connections > 0 to trigger drain broadcast
Phoenix.PubSub.subscribe(Aprsme.PubSub, "connection:drain:#{Node.self()}")
:sys.replace_state(pid, fn state ->
%{state | draining: true, local_connections: 50}
end)
# Trigger check_load to run the full analysis pipeline
send(pid, :check_load)
Process.sleep(200)
assert Process.alive?(pid)
assert %{connections: 50} = ConnectionMonitor.get_stats()
end
test "get_stats returns draining field", %{pid: pid} do
:sys.replace_state(pid, fn state -> %{state | draining: true} end)
stats = ConnectionMonitor.get_stats()
assert stats.draining == true
end
test "gather_cluster_stats includes local node stats without deadlock", %{pid: pid} do
ConnectionMonitor.register_connection()
ConnectionMonitor.register_connection()
# Trigger check_load which calls gather_cluster_stats internally.
# Before the fix, this would deadlock for 5s because gather_cluster_stats
# did an RPC call to Node.self() which GenServer.call'd back into itself.
send(pid, :check_load)
Process.sleep(100)
state = :sys.get_state(pid)
# Local node stats must always be present
assert Map.has_key?(state.node_stats, Node.self())
assert state.node_stats[Node.self()].connections == 2
end
end
end