Skip to content

Commit f0be5ed

Browse files
committed
switch to a dead pid in elixir ws tests to force :noproc exit
1 parent 3102e09 commit f0be5ed

1 file changed

Lines changed: 29 additions & 15 deletions

File tree

elixir/test/ws_transport_test.exs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -295,43 +295,57 @@ defmodule DatagroutConduit.Transport.WsTest do
295295
assert state.pings_sent == 0
296296
end
297297

298-
test ":ping_tick degrades gracefully when conn_pid is a non-WebSockex process" do
299-
# WebSockex.send_frame `exit`s when the target is not a WebSockex
300-
# process; safe_send_ping/1 must catch that so the Ws GenServer
301-
# doesn't crash on every disconnect.
302-
{:ok, dummy} = Agent.start_link(fn -> nil end)
303-
state = %Ws{conn_pid: dummy, ping_interval_ms: 50_000}
298+
# Helper: returns a freshly-dead pid. `WebSockex.send_frame/2` exits
299+
# with `:noproc` immediately when called against a dead pid — exactly
300+
# the production crash scenario `safe_send_ping/1` is designed to
301+
# catch — and it avoids both the 5-second `:gen.call` timeout and the
302+
# noisy `[error] Agent.Server received unexpected message …` lines
303+
# that an `Agent.start_link/1` dummy would emit when poked with the
304+
# `:"$websockex_send"` protocol.
305+
defp dead_pid do
306+
pid = spawn(fn -> :ok end)
307+
ref = Process.monitor(pid)
308+
309+
receive do
310+
{:DOWN, ^ref, :process, ^pid, _} -> :ok
311+
after
312+
500 -> flunk("spawned process did not exit in time")
313+
end
314+
315+
pid
316+
end
317+
318+
test ":ping_tick degrades gracefully when the Conn process has died" do
319+
# WebSockex.send_frame `exit`s when the target Conn is gone;
320+
# safe_send_ping/1 must catch that so the Ws GenServer doesn't
321+
# crash alongside it.
322+
state = %Ws{conn_pid: dead_pid(), ping_interval_ms: 50_000}
304323

305324
assert {:noreply, new_state} = Ws.handle_info(:ping_tick, state)
306325

307326
# Send failed, so pings_sent stays at 0 — but the GenServer survived.
308327
assert new_state.pings_sent == 0
309-
Agent.stop(dummy)
310328
end
311329

312330
test ":ping_tick reschedules itself at ping_interval_ms" do
313-
# Use a non-WebSockex conn so the send fails gracefully; we only care
314-
# about the reschedule signal here.
315-
{:ok, dummy} = Agent.start_link(fn -> nil end)
316-
state = %Ws{conn_pid: dummy, ping_interval_ms: 30}
331+
# Dead conn so the send fails fast; we only care about the
332+
# reschedule signal here.
333+
state = %Ws{conn_pid: dead_pid(), ping_interval_ms: 30}
317334

318335
# First tick fires immediately via direct handle_info call.
319336
{:noreply, _state} = Ws.handle_info(:ping_tick, state)
320337

321338
# Production handler scheduled the NEXT tick — verify it lands in
322339
# this process's mailbox within ~100ms.
323340
assert_receive :ping_tick, 200
324-
Agent.stop(dummy)
325341
end
326342

327343
test "ping_interval_ms = 0 disables rescheduling (no further tick)" do
328-
{:ok, dummy} = Agent.start_link(fn -> nil end)
329-
state = %Ws{conn_pid: dummy, ping_interval_ms: 0}
344+
state = %Ws{conn_pid: dead_pid(), ping_interval_ms: 0}
330345

331346
{:noreply, _state} = Ws.handle_info(:ping_tick, state)
332347

333348
refute_receive :ping_tick, 100
334-
Agent.stop(dummy)
335349
end
336350

337351
test "pings_sent/1 returns the live counter through the GenServer API" do

0 commit comments

Comments
 (0)