- 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.
89 lines
2.9 KiB
Elixir
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
|