Root cause of the order/seed-dependent integration failures: when the WS
upgrade's 101 response and the server's first frame (HELLO) arrive in one TCP
segment, Mint returns them as [status, headers, {:data, hello}, {:done}] - the
{:data} ordered BEFORE the {:done} that builds the websocket. process_responses
only decoded {:data} once the websocket existed, so the coalesced HELLO was
silently dropped and the client wedged in :hello_wait. Coalescing is far more
likely under full-suite scheduler load, which also starved the cadence-sensitive
tests - hence the flaky, seed-varying failures. Buffer pre-upgrade bytes and
decode them the instant the websocket is built.
Test hermeticity:
- Add Dexcord.EnvSandbox: snapshot all :dexcord app env in setup, restore
wholesale on_exit. Replaces every file's hand-rolled per-key save/restore so no
knob (timing bounds, budgets, ratelimit clock, base URLs) can leak between tests.
- gateway_fatal_test: await the directly-started supervisor's full termination
(monitor + DOWN) instead of a fire-and-forget Process.exit, so its globally-named
singletons and named ETS are gone before the next test's tree starts.
41 lines
1.6 KiB
Elixir
41 lines
1.6 KiB
Elixir
defmodule Dexcord.EnvSandbox do
|
|
@moduledoc false
|
|
# A single, bulletproof application-env fixture for the test suite.
|
|
#
|
|
# Many tests tune `:dexcord` application env (timing knobs, budgets, rate-limit
|
|
# clocks, base URLs, ...) to exercise real logic quickly. Any key a test forgets
|
|
# to restore leaks into whatever test runs next and produces order-dependent,
|
|
# seed-sensitive failures. Rather than have every file maintain its own hand-rolled
|
|
# save/restore list (and get it subtly wrong), tests call `sandbox_env/0` once in
|
|
# `setup`: it snapshots the ENTIRE `:dexcord` env up front and, via `on_exit`,
|
|
# restores it wholesale afterwards - deleting any key the test introduced and
|
|
# resetting every pre-existing key to its captured value. Tests then `put_env`
|
|
# freely with no per-key bookkeeping.
|
|
|
|
import ExUnit.Callbacks, only: [on_exit: 1]
|
|
|
|
@doc """
|
|
Snapshots all `:dexcord` application env and registers wholesale restoration on
|
|
test exit. Call once from a test's `setup`.
|
|
"""
|
|
@spec sandbox_env :: :ok
|
|
def sandbox_env do
|
|
before = Application.get_all_env(:dexcord)
|
|
before_keys = MapSet.new(before, fn {k, _v} -> k end)
|
|
|
|
on_exit(fn ->
|
|
# Drop any key the test introduced that wasn't there before.
|
|
for {key, _val} <- Application.get_all_env(:dexcord),
|
|
not MapSet.member?(before_keys, key) do
|
|
Application.delete_env(:dexcord, key)
|
|
end
|
|
|
|
# Restore every snapshotted key to exactly its captured value.
|
|
for {key, val} <- before do
|
|
Application.put_env(:dexcord, key, val)
|
|
end
|
|
end)
|
|
|
|
:ok
|
|
end
|
|
end
|