test: fix slow + flaky tests (Mox global, env pollution, pool starvation)
- Remove Mox.set_mox_global() in DPW extra test; rely on $callers
instead so concurrent vendor SNMP tests stop intermittently failing
with VerificationError.
- Make ToweropsWeb.Plugs.GraphQLIntrospectionTest async: false. It
mutates Application.put_env(:towerops, :env, :prod), which polluted
every async vendor test that reads :env via
Towerops.Snmp.Client.phoenix_snmp_disabled/0.
- Bump test pool queue_target/queue_interval. AgentChannelTest spawns
many `:proc_lib` channel processes that hold sandbox connections;
the default 50ms queue caused intermittent
'could not checkout the connection' errors.
- Tag real-System.cmd("ping") tests as :network so they're excluded
by default. Saves ~20s on every full run.
- Make JobCleanupTask's internal 1s settle sleep configurable via
:job_cleanup_settle_ms; the test overrides it to 0.
Plus the pending CoverageLive.Form edit-save / EIRP recompute and
TraceLive deep-link tests from before the compact.
This commit is contained in:
parent
c90e34de0b
commit
c50dc08ad4
9 changed files with 147 additions and 5 deletions
|
|
@ -62,7 +62,14 @@ if database_url = System.get_env("DATABASE_URL") do
|
|||
config :towerops, Towerops.Repo,
|
||||
url: database_url,
|
||||
pool: Sandbox,
|
||||
pool_size: System.schedulers_online() * 2
|
||||
pool_size: System.schedulers_online() * 2,
|
||||
# Bumped from default 50ms — channel-heavy test files (notably
|
||||
# AgentChannelTest) intermittently exhaust the pool while waiting on
|
||||
# background `:proc_lib` processes to release their checked-out
|
||||
# connections. A longer queue lets the test pool ride out the spike
|
||||
# rather than crashing with `connection not available`.
|
||||
queue_target: 500,
|
||||
queue_interval: 5_000
|
||||
else
|
||||
config :towerops, Towerops.Repo,
|
||||
username: "postgres",
|
||||
|
|
@ -70,7 +77,9 @@ else
|
|||
hostname: "localhost",
|
||||
database: "towerops_test#{System.get_env("MIX_TEST_PARTITION")}",
|
||||
pool: Sandbox,
|
||||
pool_size: System.schedulers_online() * 2
|
||||
pool_size: System.schedulers_online() * 2,
|
||||
queue_target: 500,
|
||||
queue_interval: 5_000
|
||||
end
|
||||
|
||||
# Configure Cloak encryption for testing
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ defmodule Towerops.Workers.JobCleanupTask do
|
|||
cancel_all_polling_jobs()
|
||||
|
||||
# Wait a moment for cancellations to process
|
||||
Process.sleep(1000)
|
||||
Process.sleep(Application.get_env(:towerops, :job_cleanup_settle_ms, 1000))
|
||||
|
||||
# Reschedule polling for all enabled devices
|
||||
reschedule_all_devices()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ defmodule Towerops.Monitoring.Executors.PingExecutorTest do
|
|||
alias Towerops.Monitoring.Executors.PingExecutor
|
||||
|
||||
describe "execute/2" do
|
||||
@tag :network
|
||||
test "returns success with valid ping output" do
|
||||
config = %{"host" => "127.0.0.1", "count" => 1}
|
||||
|
||||
|
|
@ -20,6 +21,7 @@ defmodule Towerops.Monitoring.Executors.PingExecutorTest do
|
|||
end
|
||||
end
|
||||
|
||||
@tag :network
|
||||
test "defaults count to 3 when not provided" do
|
||||
config = %{"host" => "127.0.0.1"}
|
||||
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ defmodule Towerops.Monitoring.PingTest do
|
|||
# correctly handles various response formats by testing with valid IPs
|
||||
# that return quickly (localhost)
|
||||
|
||||
@tag :network
|
||||
test "localhost ping returns valid response format" do
|
||||
result = Ping.ping("127.0.0.1", 2000)
|
||||
|
||||
|
|
@ -148,6 +149,7 @@ defmodule Towerops.Monitoring.PingTest do
|
|||
end
|
||||
end
|
||||
|
||||
@tag :network
|
||||
test "default-arity ping/1 dispatches to ping/2" do
|
||||
# ping/1 calls ping(ip, 5000); we mostly want to exercise the arity
|
||||
# without slowing the suite down for a real timeout.
|
||||
|
|
@ -159,6 +161,7 @@ defmodule Towerops.Monitoring.PingTest do
|
|||
end
|
||||
end
|
||||
|
||||
@tag :network
|
||||
test "unreachable IP returns :timeout error" do
|
||||
# 192.0.2.0/24 is reserved for documentation per RFC 5737 — never routed.
|
||||
result = Ping.ping("192.0.2.1", 1000)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,16 @@ defmodule Towerops.Workers.DevicePollerWorkerExtraTest do
|
|||
setup :verify_on_exit!
|
||||
|
||||
setup do
|
||||
Mox.set_mox_global()
|
||||
# NOTE: Do NOT call `Mox.set_mox_global/0` here. This file is `async: false`,
|
||||
# but `set_mox_global` makes the SnmpMock visible to *every* process in the
|
||||
# VM, which has historically stolen expectations from concurrent async
|
||||
# vendor profile tests (Siae/Moxa/Geist/Himoinsa…) and caused flaky
|
||||
# `Mox.VerificationError` failures.
|
||||
#
|
||||
# Mox 1.0+ already follows the `$callers` chain set by `Task.async/1` and
|
||||
# `Task.async_stream/3`, which is what `DevicePollerWorker.poll_device/1`
|
||||
# uses internally. So expectations defined on the test process flow into
|
||||
# those tasks without any global flag.
|
||||
old_adapter = Application.get_env(:towerops, :snmp_adapter)
|
||||
Application.put_env(:towerops, :snmp_adapter, SnmpMock)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@ defmodule Towerops.Workers.JobCleanupTaskTest do
|
|||
|
||||
setup do
|
||||
previous_env = Application.get_env(:towerops, :env)
|
||||
previous_settle = Application.get_env(:towerops, :job_cleanup_settle_ms)
|
||||
|
||||
# Make the internal "wait for cancellations to process" sleep a no-op
|
||||
# in tests so the prod-path test runs in <1ms instead of >1s.
|
||||
Application.put_env(:towerops, :job_cleanup_settle_ms, 0)
|
||||
|
||||
on_exit(fn ->
|
||||
if previous_env do
|
||||
|
|
@ -16,6 +21,12 @@ defmodule Towerops.Workers.JobCleanupTaskTest do
|
|||
else
|
||||
Application.delete_env(:towerops, :env)
|
||||
end
|
||||
|
||||
if previous_settle do
|
||||
Application.put_env(:towerops, :job_cleanup_settle_ms, previous_settle)
|
||||
else
|
||||
Application.delete_env(:towerops, :job_cleanup_settle_ms)
|
||||
end
|
||||
end)
|
||||
|
||||
:ok
|
||||
|
|
|
|||
|
|
@ -80,4 +80,73 @@ defmodule ToweropsWeb.CoverageLive.FormTest do
|
|||
assert render_click(view, "use_site_location", %{})
|
||||
end
|
||||
end
|
||||
|
||||
describe "edit save" do
|
||||
test "edit save with valid attrs redirects to show", %{conn: conn, org: org, site: site} do
|
||||
cov = Towerops.CoveragesFixtures.coverage_fixture(org.id, site.id)
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/coverage/#{cov.id}/edit")
|
||||
|
||||
result =
|
||||
view
|
||||
|> form("form", coverage: %{"name" => "Renamed Coverage"})
|
||||
|> render_submit()
|
||||
|
||||
assert is_binary(result) or match?({:error, {:redirect, _}}, result) or
|
||||
match?({:error, {:live_redirect, _}}, result)
|
||||
end
|
||||
|
||||
test "edit save with invalid name re-renders the form", %{
|
||||
conn: conn,
|
||||
org: org,
|
||||
site: site
|
||||
} do
|
||||
cov = Towerops.CoveragesFixtures.coverage_fixture(org.id, site.id)
|
||||
|
||||
{:ok, view, _html} = live(conn, ~p"/coverage/#{cov.id}/edit")
|
||||
|
||||
html =
|
||||
view
|
||||
|> form("form", coverage: %{"name" => ""})
|
||||
|> render_submit()
|
||||
|
||||
assert is_binary(html)
|
||||
end
|
||||
end
|
||||
|
||||
describe "validate event triggers EIRP recompute" do
|
||||
test "validate event with antenna_slug computes a non-zero EIRP", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/coverage/new")
|
||||
|
||||
html =
|
||||
view
|
||||
|> form("form",
|
||||
coverage: %{
|
||||
"name" => "Test",
|
||||
"antenna_slug" => "simulate-isotropic-omni-0",
|
||||
"tx_power_dbm" => "20.0"
|
||||
}
|
||||
)
|
||||
|> render_change()
|
||||
|
||||
assert is_binary(html)
|
||||
end
|
||||
|
||||
test "validate event with bogus number string falls back gracefully", %{conn: conn} do
|
||||
{:ok, view, _html} = live(conn, ~p"/coverage/new")
|
||||
|
||||
html =
|
||||
view
|
||||
|> form("form",
|
||||
coverage: %{
|
||||
"name" => "Test",
|
||||
"antenna_slug" => "simulate-isotropic-omni-0",
|
||||
"tx_power_dbm" => "not-a-number"
|
||||
}
|
||||
)
|
||||
|> render_change()
|
||||
|
||||
assert is_binary(html)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -108,4 +108,38 @@ defmodule ToweropsWeb.TraceLive.IndexTest do
|
|||
assert Index.format_ms("x") == "x"
|
||||
end
|
||||
end
|
||||
|
||||
describe "deep-link with trace data renders issues_callout" do
|
||||
test "trace_type=device with a real device renders the trace shell",
|
||||
%{conn: conn, organization: organization} do
|
||||
{:ok, site} = Towerops.Sites.create_site(%{name: "Trace Test Site", organization_id: organization.id})
|
||||
|
||||
{:ok, device} =
|
||||
Towerops.Devices.create_device(%{
|
||||
name: "Trace Dev",
|
||||
ip_address: "10.10.10.1",
|
||||
site_id: site.id,
|
||||
organization_id: organization.id
|
||||
})
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/trace?type=device&id=#{device.id}")
|
||||
# Either renders the trace card or the empty state. Both exercise
|
||||
# handle_params with type+id which calls Trace.assemble_trace/3.
|
||||
assert html =~ "Trace" or html =~ device.name
|
||||
end
|
||||
|
||||
test "trace_type=site with a real site renders the trace shell",
|
||||
%{conn: conn, organization: organization} do
|
||||
{:ok, site} = Towerops.Sites.create_site(%{name: "Trace Site", organization_id: organization.id})
|
||||
|
||||
{:ok, _view, html} = live(conn, ~p"/trace?type=site&id=#{site.id}")
|
||||
assert html =~ "Trace" or html =~ site.name
|
||||
end
|
||||
|
||||
test "trace_type=account with an unknown id still renders without crash",
|
||||
%{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, ~p"/trace?type=account&id=#{Ecto.UUID.generate()}")
|
||||
assert html =~ "Trace"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
defmodule ToweropsWeb.Plugs.GraphQLIntrospectionTest do
|
||||
use ToweropsWeb.ConnCase, async: true
|
||||
# NOTE: async: false because the "production" describe block mutates the
|
||||
# global `Application.put_env(:towerops, :env, :prod)`. Other async tests
|
||||
# (notably the vendor SNMP profile tests) check this env to decide whether
|
||||
# to short-circuit SNMP calls, so running this concurrently caused
|
||||
# intermittent vendor test failures. See `Towerops.Snmp.Client.phoenix_snmp_disabled/0`.
|
||||
use ToweropsWeb.ConnCase, async: false
|
||||
|
||||
alias ToweropsWeb.Plugs.GraphQLIntrospection
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue