tests: disable Req retries in test env to eliminate ~7s-per-test delays

Req's default `retry: :safe_transient` waits ~7s across exponential backoff
when the stubbed response is a 5xx or connection error. Tests that asserted
error-status behavior paid that price on every run.

Disable retries in test env for every direct Req caller:

* `Towerops.HTTP` (covers weather/netbox/geocoding/http_executor/pagerduty)
* `CnMaestro.Client`, `Gaiia.Client`, `Preseem.Client`, `Sonar.Client`,
  `Splynx.Client`, `Agents.ReleaseChecker`, `Billing.StripeClient`

Use `Keyword.put_new(:retry, false)` so any test explicitly opting into
retry behavior is respected.

Suite time: 82.8s → 65.3s. Slowest test dropped from 7050ms to 1447ms.
This commit is contained in:
Graham McIntire 2026-04-24 10:04:52 -05:00
parent 3ca0834ef0
commit 85dd821400
8 changed files with 48 additions and 11 deletions

View file

@ -133,7 +133,9 @@ defmodule Towerops.Agents.ReleaseChecker do
defp req_get(url, opts) do
opts =
if Application.get_env(:towerops, :env) == :test do
Keyword.put(opts, :plug, {Req.Test, __MODULE__})
opts
|> Keyword.put(:plug, {Req.Test, __MODULE__})
|> Keyword.put_new(:retry, false)
else
opts
end

View file

@ -216,10 +216,14 @@ defmodule Towerops.Billing.StripeClient do
decode_json: [keys: :strings]
] ++ opts
# Use Req.Test in test environment (following existing pattern)
# Use Req.Test in test environment (following existing pattern).
# Also disable retries in test so stubbed 5xx/429 responses return
# immediately instead of spending seconds retrying.
req_opts =
if Application.get_env(:towerops, :env) == :test do
Keyword.put(req_opts, :plug, {Req.Test, __MODULE__})
req_opts
|> Keyword.put(:plug, {Req.Test, __MODULE__})
|> Keyword.put_new(:retry, false)
else
req_opts
end

View file

@ -165,8 +165,15 @@ defmodule Towerops.CnMaestro.Client do
e -> {:error, Exception.message(e)}
end
defp maybe_add_test_plug(opts), do: maybe_attach_test_plug(opts, Application.get_env(:towerops, :env))
defp maybe_add_test_plug(opts) do
opts
|> maybe_attach_test_plug(Application.get_env(:towerops, :env))
|> maybe_disable_retry(Application.get_env(:towerops, :env))
end
defp maybe_attach_test_plug(opts, :test), do: Keyword.put(opts, :plug, {Req.Test, __MODULE__})
defp maybe_attach_test_plug(opts, _env), do: opts
defp maybe_disable_retry(opts, :test), do: Keyword.put_new(opts, :retry, false)
defp maybe_disable_retry(opts, _env), do: opts
end

View file

@ -318,7 +318,9 @@ defmodule Towerops.Gaiia.Client do
req_opts =
if Application.get_env(:towerops, :env) == :test do
Keyword.put(req_opts, :plug, {Req.Test, __MODULE__})
req_opts
|> Keyword.put(:plug, {Req.Test, __MODULE__})
|> Keyword.put_new(:retry, false)
else
req_opts
end

View file

@ -4,7 +4,7 @@ defmodule Towerops.HTTP do
def request(owner, req_opts) when is_list(req_opts) do
req_opts
|> maybe_attach_test_plug(owner)
# |> maybe_disable_retry() # Temporarily disabled to debug PagerDuty tests
|> maybe_disable_retry()
|> Req.request()
rescue
error in RuntimeError ->
@ -42,4 +42,15 @@ defmodule Towerops.HTTP do
req_opts
end
end
# In test, disable Req's default retry-on-5xx/transient-error behavior.
# Without this, tests that stub an error status wait ~7s for retries to
# exhaust before the response is returned.
defp maybe_disable_retry(req_opts) do
if Application.get_env(:towerops, :env) == :test and !Keyword.has_key?(req_opts, :retry) do
Keyword.put(req_opts, :retry, false)
else
req_opts
end
end
end

View file

@ -116,10 +116,13 @@ defmodule Towerops.Preseem.Client do
headers: [{"accept", "application/json"}]
]
# Only use Req.Test plug in test environment
# Only use Req.Test plug in test environment; disable retry in tests
# so stubbed error statuses don't trigger a ~7s exponential backoff.
opts =
if Application.get_env(:towerops, :env) == :test do
Keyword.put(opts, :plug, {Req.Test, __MODULE__})
opts
|> Keyword.put(:plug, {Req.Test, __MODULE__})
|> Keyword.put_new(:retry, false)
else
opts
end

View file

@ -159,7 +159,11 @@ defmodule Towerops.Sonar.Client do
{:error, Exception.message(exception)}
end
defp maybe_attach_test_plug(req_opts, :test), do: Keyword.put(req_opts, :plug, {Req.Test, __MODULE__})
defp maybe_attach_test_plug(req_opts, :test) do
req_opts
|> Keyword.put(:plug, {Req.Test, __MODULE__})
|> Keyword.put_new(:retry, false)
end
defp maybe_attach_test_plug(req_opts, _env), do: req_opts

View file

@ -76,7 +76,9 @@ defmodule Towerops.Splynx.Client do
req_opts =
if Application.get_env(:towerops, :env) == :test do
Keyword.put(req_opts, :plug, {Req.Test, __MODULE__})
req_opts
|> Keyword.put(:plug, {Req.Test, __MODULE__})
|> Keyword.put_new(:retry, false)
else
req_opts
end
@ -119,7 +121,9 @@ defmodule Towerops.Splynx.Client do
req_opts =
if Application.get_env(:towerops, :env) == :test do
Keyword.put(req_opts, :plug, {Req.Test, __MODULE__})
req_opts
|> Keyword.put(:plug, {Req.Test, __MODULE__})
|> Keyword.put_new(:retry, false)
else
req_opts
end