@@ -52,7 +52,7 @@ defmodule Sentry.Test do
5252
5353 @ moduledoc since: "10.2.0"
5454
55- @ compile { :no_warn_undefined , [ Bypass , Plug.Conn ] }
55+ @ compile { :no_warn_undefined , [ Bypass , Plug.Conn , :telemetry ] }
5656
5757 @ ownership_server Sentry.Test.OwnershipServer
5858
@@ -75,8 +75,13 @@ defmodule Sentry.Test do
7575
7676 ## Options
7777
78- Any extra Sentry config options (e.g., `dedup_events: false`, `traces_sample_rate: 1.0`)
79- will be forwarded to the test config.
78+ * `:allowance` - a list of integration module atoms to enable automatic
79+ `Sentry.Test.allow_sentry_reports/2` wiring for. The integrations land
80+ in follow-up commits; see the integration-specific sections below for
81+ supported entries.
82+
83+ Any other key is forwarded to the per-test Sentry config (e.g.,
84+ `dedup_events: false`, `traces_sample_rate: 1.0`).
8085
8186 The reserved `:telemetry_processor` option is *not* forwarded to the test
8287 config. Instead, its value (a keyword list) is passed to the per-test
@@ -135,6 +140,7 @@ defmodule Sentry.Test do
135140
136141 { tp_opts , extra_config } = Keyword . pop ( extra_config , :telemetry_processor , [ ] )
137142 { collect_envelopes , extra_config } = Keyword . pop ( extra_config , :collect_envelopes , false )
143+ { allowance , extra_config } = Keyword . pop ( extra_config , :allowance , [ ] )
138144
139145 # Open a per-test Bypass and stub the envelope endpoint
140146 bypass = Bypass . open ( )
@@ -151,6 +157,8 @@ defmodule Sentry.Test do
151157 bypass_config = [ dsn: "http://public:secret@localhost:#{ bypass . port } /1" ]
152158 setup_collector ( bypass_config ++ extra_config )
153159
160+ attach_allowance_handlers ( allowance , self ( ) )
161+
154162 case collect_envelopes do
155163 false ->
156164 % { bypass: bypass , telemetry_processor: processor_name }
@@ -835,6 +843,81 @@ defmodule Sentry.Test do
835843 end
836844 end
837845
846+ defp ensure_telemetry_loaded! do
847+ unless Code . ensure_loaded? ( :telemetry ) do
848+ raise """
849+ `:telemetry` is required for the `:allowance` option of Sentry.Test
850+ but is not available. Add it to your test dependencies:
851+
852+ {:telemetry, "~> 1.0", only: [:test]}
853+ """
854+ end
855+ end
856+
857+ # ── :allowance plumbing ──
858+ #
859+ # Each integration atom (e.g. Oban, Broadway) is mapped by
860+ # allowance_handlers!/1 to one or more {telemetry_event, {module, fun}}
861+ # pairs. Commit 1 ships only the catch-all clause; commits 2 and 3 add
862+ # the integration-specific clauses.
863+
864+ defp attach_allowance_handlers ( [ ] , _owner_pid ) , do: :ok
865+
866+ defp attach_allowance_handlers ( modules , owner_pid ) when is_list ( modules ) do
867+ ensure_telemetry_loaded! ( )
868+ Enum . each ( modules , & attach_allowance_handler ( & 1 , owner_pid ) )
869+ end
870+
871+ defp attach_allowance_handler ( module , owner_pid ) do
872+ case allowance_handlers ( module ) do
873+ :unknown ->
874+ raise ArgumentError ,
875+ "unknown :allowance entry #{ inspect ( module ) } . Supported integrations: " <>
876+ "(none built-in yet — Oban and Broadway land in follow-up commits)"
877+
878+ pairs when is_list ( pairs ) ->
879+ Enum . each ( pairs , fn { event , handler_fun } ->
880+ __attach_allowance__ ( event , handler_fun , % { owner_pid: owner_pid } )
881+ end )
882+ end
883+ end
884+
885+ @ doc false
886+ @ spec __attach_allowance__ ( [ atom ( ) ] , { module ( ) , atom ( ) } , map ( ) ) :: :ok
887+ def __attach_allowance__ ( event , { module , function } , config )
888+ when is_list ( event ) and is_atom ( module ) and is_atom ( function ) and is_map ( config ) do
889+ ref = System . unique_integer ( [ :positive ] )
890+ handler_id = { :sentry_test_allowance , ref }
891+
892+ :ok =
893+ :telemetry . attach (
894+ handler_id ,
895+ event ,
896+ Function . capture ( module , function , 4 ) ,
897+ config
898+ )
899+
900+ ExUnit.Callbacks . on_exit ( fn -> :telemetry . detach ( handler_id ) end )
901+ :ok
902+ end
903+
904+ # Generic "allow whatever fired this event for owner_pid" handler. Used
905+ # by the foundation unit tests and available for ad-hoc telemetry
906+ # routing; the Oban / Broadway dispatches use their own handlers that
907+ # consult metadata rather than blindly allowing the emitting pid.
908+ @ doc false
909+ def __handle_allowance_event__ ( _event , _measurements , _metadata , % { owner_pid: owner_pid } ) do
910+ allow_sentry_reports ( owner_pid , self ( ) )
911+ rescue
912+ ArgumentError -> :ok
913+ end
914+
915+ # Returns the list of `{event_path, {module, function}}` handler pairs for
916+ # a given integration atom, or `:unknown` for unsupported entries (the
917+ # caller turns that into an `ArgumentError`). Commits 2 and 3 prepend
918+ # clauses for `Oban` and `Broadway` respectively.
919+ defp allowance_handlers ( _other ) , do: :unknown
920+
838921 # Sets up collection infrastructure (ETS table, before_send wrapping, config)
839922 # without opening a new Bypass. When no :dsn is provided in extra_config,
840923 # falls back to the default Bypass DSN from Registry.
0 commit comments