- Bug #12: Lower rate limit to 15/min - Bug #13: Wrap model loading in Task.start - Bug #14: Fix classify_time_period guard gap at -3.0 - Bug #15: Not applicable (Elixir has no ?? operator) - Bug #16: Remove fallback Repo.get for station preload - Bug #17: Extract cache_key() in ContactMapController - Bug #18: Add has_many :contacts and :beacons to User schema - A&D #4: Move serve_markdown_if_requested after secure headers - Config #1: Move signing_salt to runtime.exs env var - Config #2: Use --check-unused instead of --unused - Config #3: Re-enable UnsafeToAtom Credo check - Config #5: Re-enable LeakyEnvironment Credo check - Add @spec annotations to fix re-enabled Specs violations - Replace String.to_atom with to_existing_atom where guarded
103 lines
3.7 KiB
Elixir
103 lines
3.7 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 is_list(metrics)
|
|
assert Enum.any?(metrics)
|
|
|
|
for m <- metrics do
|
|
assert is_struct(m)
|
|
# Every metric carries its event path as a list of atoms.
|
|
assert is_list(m.event_name)
|
|
assert Enum.all?(m.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 is_binary(meta.queue)
|
|
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 "starts a named Supervisor under a fresh registry" do
|
|
# Different process name to avoid colliding with the already-running
|
|
# application supervisor.
|
|
# credo:disable-for-this-line Credo.Check.Warning.UnsafeToAtom
|
|
sup_name = :"telemetry_under_test_#{System.unique_integer([:positive])}"
|
|
|
|
{:ok, pid} = Supervisor.start_link(Telemetry, :ok, name: sup_name)
|
|
assert Process.whereis(sup_name) == pid
|
|
|
|
# Unlink before tearing down so the brutal-kill exit signal doesn't
|
|
# propagate to the test process (which trap_exit defaults to off).
|
|
Process.unlink(pid)
|
|
Process.exit(pid, :kill)
|
|
end
|
|
end
|
|
end
|