defmodule Microwaveprop.InstrumentTest do @moduledoc """ Cover the `Instrument.span/3` wrapper directly. It's an unassuming 5-line function but it's the emit point every call-site telemetry handler keys off, so regressions here are invisible at the call site but silently break metrics. """ use ExUnit.Case, async: false alias Microwaveprop.Instrument setup do test_pid = self() handler_id = {__MODULE__, :instrument_handler, System.unique_integer([:positive])} :telemetry.attach_many( handler_id, [ [:microwaveprop, :test, :start], [:microwaveprop, :test, :stop], [:microwaveprop, :test, :exception] ], fn event, measurements, metadata, _config -> send(test_pid, {:telemetry, event, measurements, metadata}) end, nil ) on_exit(fn -> :telemetry.detach(handler_id) end) :ok end test "wraps the function in a telemetry span and returns its value" do assert 42 == Instrument.span([:test], %{flavour: :unit}, fn -> 42 end) assert_receive {:telemetry, [:microwaveprop, :test, :start], %{}, %{flavour: :unit}} assert_receive {:telemetry, [:microwaveprop, :test, :stop], %{duration: dur}, stop_meta} assert dur >= 0 assert stop_meta.flavour == :unit assert stop_meta.result == :ok end test "supports metadata default of %{}" do assert :ok == Instrument.span([:test], fn -> :ok end) assert_receive {:telemetry, [:microwaveprop, :test, :start], %{}, %{}} assert_receive {:telemetry, [:microwaveprop, :test, :stop], %{duration: _}, stop_meta} assert stop_meta.result == :ok end test "raising inside the span emits an exception event and re-raises" do assert_raise RuntimeError, "boom", fn -> Instrument.span([:test], %{}, fn -> raise "boom" end) end assert_receive {:telemetry, [:microwaveprop, :test, :start], _, _} assert_receive {:telemetry, [:microwaveprop, :test, :exception], %{duration: _}, exception_meta} # :telemetry.span/3 adds `kind`, `reason`, `stacktrace` keys on the # exception branch — assert the ones worth relying on. assert exception_meta.kind == :error assert %RuntimeError{message: "boom"} = exception_meta.reason end test "prefixes every event suffix with :microwaveprop" do # The whole point of the wrapper is that call sites pass [:foo, :bar] # and get telemetry rooted at [:microwaveprop, :foo, :bar]. Verify by # attaching a separate handler on a distinct suffix. test_pid = self() handler_id = {__MODULE__, :prefix_check, System.unique_integer([:positive])} :telemetry.attach( handler_id, [:microwaveprop, :prefix, :check, :stop], fn event, _m, _md, _c -> send(test_pid, {:evt, event}) end, nil ) try do :ok = Instrument.span([:prefix, :check], fn -> :ok end) assert_receive {:evt, [:microwaveprop, :prefix, :check, :stop]} after :telemetry.detach(handler_id) end end end