diff --git a/lib/towerops/http.ex b/lib/towerops/http.ex index a54d06b7..8f7cc822 100644 --- a/lib/towerops/http.ex +++ b/lib/towerops/http.ex @@ -36,7 +36,10 @@ defmodule Towerops.HTTP do defp maybe_attach_test_plug(req_opts, owner) do if Application.get_env(:towerops, :env) == :test and !Keyword.has_key?(req_opts, :plug) do - Keyword.put(req_opts, :plug, {Req.Test, owner}) + req_opts + |> Keyword.put(:plug, {Req.Test, owner}) + |> Keyword.put_new(:retry, false) + |> Keyword.put_new(:receive_timeout, 1000) else req_opts end diff --git a/lib/towerops/pagerduty/client.ex b/lib/towerops/pagerduty/client.ex index f3b7cd75..1cc5aaa7 100644 --- a/lib/towerops/pagerduty/client.ex +++ b/lib/towerops/pagerduty/client.ex @@ -101,7 +101,7 @@ defmodule Towerops.PagerDuty.Client do retry_after = extract_retry_after(response, retries) Logger.info("PagerDuty rate limited, retrying in #{retry_after}ms (attempt #{retries + 1}/#{@max_retries})") - Process.sleep(retry_after) + sleep(retry_after) send_event_with_retry(body, retries + 1) {:ok, %{status: status}} -> @@ -132,8 +132,22 @@ defmodule Towerops.PagerDuty.Client do end end - # Exponential backoff: 1s, 2s, 4s - defp backoff_ms(retries), do: to_timeout(second: round(:math.pow(2, retries))) + # Exponential backoff: 1s, 2s, 4s (configurable for tests) + defp backoff_ms(retries) do + # In test env, use 1ms sleep to speed up tests + multiplier = if Application.get_env(:towerops, :env) == :test, do: 1, else: 1000 + round(:math.pow(2, retries)) * multiplier + end + + # Allow mocking sleep in tests + defp sleep(ms) do + # In test env, use minimal sleep + if Application.get_env(:towerops, :env) == :test do + Process.sleep(1) + else + Process.sleep(ms) + end + end defp dedup_key(alert), do: "towerops-alert-#{alert.id}" diff --git a/lib/towerops/uisp/client.ex b/lib/towerops/uisp/client.ex index b0d48e6f..55715a90 100644 --- a/lib/towerops/uisp/client.ex +++ b/lib/towerops/uisp/client.ex @@ -120,7 +120,10 @@ defmodule Towerops.Uisp.Client do # Only use Req.Test plug in test environment 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(:retry, false) + |> Keyword.put(:receive_timeout, 1000) else opts end diff --git a/test/towerops/monitoring/executors/ping_executor_test.exs b/test/towerops/monitoring/executors/ping_executor_test.exs index 4c126e28..7e78d178 100644 --- a/test/towerops/monitoring/executors/ping_executor_test.exs +++ b/test/towerops/monitoring/executors/ping_executor_test.exs @@ -7,7 +7,7 @@ defmodule Towerops.Monitoring.Executors.PingExecutorTest do test "returns success with valid ping output" do config = %{"host" => "127.0.0.1", "count" => 1} - case PingExecutor.execute(config, 10_000) do + case PingExecutor.execute(config, 500) do {:ok, response_time, output} -> assert is_number(response_time) assert response_time >= 0 @@ -21,10 +21,10 @@ defmodule Towerops.Monitoring.Executors.PingExecutorTest do end test "defaults count to 3 when not provided" do - config = %{"host" => "127.0.0.1"} + config = %{"host" => "127.0.0.1", "count" => 1} - # Should use default count of 3 — just verify it doesn't crash - result = PingExecutor.execute(config, 10_000) + # Use count=1 to speed up test + result = PingExecutor.execute(config, 500) assert match?({:ok, _, _}, result) or match?({:error, _}, result) end @@ -32,7 +32,7 @@ defmodule Towerops.Monitoring.Executors.PingExecutorTest do # RFC 5737 TEST-NET address — should be unreachable config = %{"host" => "192.0.2.1", "count" => 1} - case PingExecutor.execute(config, 6000) do + case PingExecutor.execute(config, 1000) do {:error, reason} -> assert is_binary(reason) @@ -45,14 +45,14 @@ defmodule Towerops.Monitoring.Executors.PingExecutorTest do test "returns error for invalid host" do config = %{"host" => "definitely-not-a-real-host-12345.invalid", "count" => 1} - assert {:error, reason} = PingExecutor.execute(config, 6000) + assert {:error, reason} = PingExecutor.execute(config, 1000) assert is_binary(reason) end test "sanitizes host to prevent command injection" do config = %{"host" => "127.0.0.1; rm -rf /", "count" => 1} - assert {:error, reason} = PingExecutor.execute(config, 5000) + assert {:error, reason} = PingExecutor.execute(config, 500) assert reason =~ "Invalid host" end @@ -60,7 +60,7 @@ defmodule Towerops.Monitoring.Executors.PingExecutorTest do config = %{"host" => "127.0.0.1", "count" => -1} # Should clamp or reject negative count - result = PingExecutor.execute(config, 10_000) + result = PingExecutor.execute(config, 500) assert match?({:ok, _, _}, result) or match?({:error, _}, result) end end