prop/test/microwaveprop/instrument_test.exs
Graham McIntire 41573d4013
test: cover Format.number/bytes, Instrument.span, SolarClient edges
- Format.number/1: boundary cases (0, 999, 1_000), float rounding,
  nil/atom fallback to to_string, plus two properties — comma
  insertion is lossless round-trip and the comma count follows
  `div(digits - 1, 3)` for |n| ≥ 1000.
- Format.bytes/1: per-unit boundary cases plus a monotonicity
  property (parse-back preserves ordering across the B/KB/MB/GB
  transitions).
- Instrument.span/2,3 (0→100%): wraps the block in a telemetry span
  returning its value, defaults metadata to %{}, emits the
  :exception event and re-raises on a raise inside the span, and
  prefixes every event suffix with :microwaveprop.
- SolarClient: short-line / empty-line / blank-day handling for
  parse_gfz_row; blank-line + short-line tolerance in parse_gfz_file;
  filter_since boundary semantics (>= is inclusive, empty list
  passes through).

Suite: 2,397 tests + 159 properties (was 2,382 + 155); coverage
71.12% → 71.16%; Format + Instrument now at 100%; credo strict clean.
2026-04-23 14:09:13 -05:00

89 lines
2.9 KiB
Elixir

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