prop/test/microwaveprop_web/telemetry_test.exs
Graham McIntire 0d00d1777c
fix: 9 bugs across tests and production code
- Fix Module.safe_concat -> Module.concat in tests with dynamic process names
  (safe_concat calls binary_to_existing_atom, but test names are newly generated)
- Fix PSKR AggregatorTest sandbox ownership by switching to async: false
- Fix MapLiveTest assert_patch: regex unsupported in LiveView 1.2, use string match
- Fix WeatherMapLiveTest toggle_grid: assert on checked attribute, not data-grid
- Fix BeaconLive.Index leaking unapproved beacons: wire data_provider to approved_beacons_query
- Fix ContactLive.Index leaking private contacts: wire data_provider with visibility filter
- Fix RecalibratorTest: train() expects factor vectors, not {vector, datetime, band} tuples
- Fix toggle_sort: compare current_field to new_field, not current_order
- Fix internal_network?: handle both atom and string session keys from Plug sessions

Test results: 3979/4001 -> 3997/4001 (18 previously-failing tests now pass)
2026-06-21 12:13:58 -05:00

101 lines
3.5 KiB
Elixir

defmodule MicrowavepropWeb.TelemetryTest do
use Microwaveprop.DataCase, async: false
alias Ecto.Adapters.SQL.Sandbox
alias MicrowavepropWeb.Telemetry
describe "metrics/0" do
test "returns a flat list of Telemetry.Metrics structs" do
metrics = Telemetry.metrics()
assert metrics != []
assert Enum.any?(metrics)
for m <- metrics do
assert %{name: _, event_name: event_name, measurement: _} = m
# Every metric carries its event path as a list of atoms.
assert event_name != []
assert Enum.all?(event_name, &is_atom/1)
end
end
test "covers each of the metric categories defined in the module" do
# The categories are private functions that build distinct event
# name prefixes; check one canonical metric per category exists.
names = Enum.map(Telemetry.metrics(), & &1.name)
name_strings = Enum.map(names, &Enum.join(&1, "."))
assert "phoenix.endpoint.stop.duration" in name_strings
assert "microwaveprop.repo.query.total_time" in name_strings
assert Enum.any?(name_strings, &String.starts_with?(&1, "vm."))
assert Enum.any?(name_strings, &String.starts_with?(&1, "microwaveprop.oban."))
end
test "every duration metric carries a unit (no bare :native measurements leak)" do
for m <- Telemetry.metrics(), String.ends_with?(to_string(List.last(m.name)), "duration") do
assert m.unit != :unit, "#{inspect(m.name)} should declare a display unit"
end
end
end
describe "dispatch_oban_queue_depth/0" do
test "emits one `oban.queue.depth` telemetry event per (queue, state) row" do
test_pid = self()
handler_id = {__MODULE__, :queue_depth_probe}
:telemetry.attach(
handler_id,
[:microwaveprop, :oban, :queue, :depth],
fn event, measurements, metadata, _cfg ->
send(test_pid, {:telemetry, event, measurements, metadata})
end,
nil
)
try do
assert :ok = Telemetry.dispatch_oban_queue_depth()
# With an empty oban_jobs table in a fresh sandbox, no events fire —
# the function still returns :ok and doesn't crash. If any rows
# happen to be there, each must carry a queue+state tag.
receive do
{:telemetry, [:microwaveprop, :oban, :queue, :depth], %{count: n}, meta} ->
assert is_integer(n)
assert byte_size(meta.queue) > 0
assert Map.has_key?(meta, :state)
after
200 -> :ok
end
after
:telemetry.detach(handler_id)
end
end
test "swallows DB errors without crashing the poller" do
# Manually close the sandbox connection so any DB query raises;
# the poller must still return :ok (telemetry is best-effort).
Sandbox.checkin(Microwaveprop.Repo)
assert :ok = Telemetry.dispatch_oban_queue_depth()
# Check a fresh connection back out so the async on_exit cleanup
# doesn't trip over the closed sandbox.
Sandbox.checkout(Microwaveprop.Repo)
end
end
describe "start_link/1" do
test "start_link/1 is defined and can be called" do
# The Telemetry supervisor uses name: __MODULE__, so it may already be
# started by the application. Verify the function handles either case.
case Telemetry.start_link(:ok) do
{:ok, pid} ->
Process.unlink(pid)
Process.exit(pid, :kill)
{:error, {:already_started, _pid}} ->
:ok
end
end
end
end