@@ -10,28 +10,44 @@ defmodule Sentry.Integrations.Oban.ErrorReporterTest do
1010 def perform ( % Oban.Job { } ) , do: :ok
1111 end
1212
13+ @ worker_as_string "Sentry.Integrations.Oban.ErrorReporterTest.MyWorker"
14+
1315 describe "handle_event/4" do
1416 test "reports the correct error to Sentry" do
15- # Any worker is okay here, this is just an easier way to get a job struct.
16- job =
17- % { "id" => "123" , "entity" => "user" , "type" => "delete" }
18- |> MyWorker . new ( )
19- |> Ecto.Changeset . apply_action! ( :validate )
20- |> Map . replace! ( :unsaved_error , % {
21- reason: % RuntimeError { message: "oops" } ,
22- kind: :error ,
23- stacktrace: [ ]
24- } )
17+ Sentry.Test . start_collecting ( )
18+
19+ emit_telemetry_for_failed_job ( :error , % RuntimeError { message: "oops" } , [ ] )
20+
21+ assert [ event ] = Sentry.Test . pop_sentry_reports ( )
22+ assert event . original_exception == % RuntimeError { message: "oops" }
23+ assert [ % { stacktrace: % { frames: [ stacktrace ] } } = exception ] = event . exception
24+
25+ assert exception . type == "RuntimeError"
26+ assert exception . value == "oops"
27+ assert exception . mechanism . handled == true
28+ assert stacktrace . module == MyWorker
29+
30+ assert stacktrace . function ==
31+ "Sentry.Integrations.Oban.ErrorReporterTest.MyWorker.process/1"
32+
33+ assert event . tags . oban_queue == "default"
34+ assert event . tags . oban_state == "available"
35+ assert event . tags . oban_worker == "Sentry.Integrations.Oban.ErrorReporterTest.MyWorker"
36+ assert % { job: % Oban.Job { } } = event . integration_meta . oban
2537
38+ assert event . fingerprint == [ @ worker_as_string , "{{ default }}" ]
39+ end
40+
41+ test "unwraps Oban.PerformErrors and reports the wrapped error" do
2642 Sentry.Test . start_collecting ( )
2743
28- assert :ok =
29- ErrorReporter . handle_event (
30- [ :oban , :job , :exception ] ,
31- % { } ,
32- % { job: job } ,
33- :no_config
34- )
44+ emit_telemetry_for_failed_job (
45+ :error ,
46+ % Oban.PerformError {
47+ reason: { :error , % RuntimeError { message: "oops" } }
48+ } ,
49+ [ ]
50+ )
3551
3652 assert [ event ] = Sentry.Test . pop_sentry_reports ( )
3753 assert event . original_exception == % RuntimeError { message: "oops" }
@@ -49,50 +65,67 @@ defmodule Sentry.Integrations.Oban.ErrorReporterTest do
4965 assert event . tags . oban_state == "available"
5066 assert event . tags . oban_worker == "Sentry.Integrations.Oban.ErrorReporterTest.MyWorker"
5167 assert % { job: % Oban.Job { } } = event . integration_meta . oban
68+
69+ assert event . fingerprint == [ @ worker_as_string , "{{ default }}" ]
5270 end
5371
5472 test "reports non-exception errors to Sentry" do
55- job =
56- % { "id" => "123" , "entity" => "user" , "type" => "delete" }
57- |> MyWorker . new ( )
58- |> Ecto.Changeset . apply_action! ( :validate )
59- |> Map . replace! ( :unsaved_error , % {
60- reason: :undef ,
61- kind: :error ,
62- stacktrace: [ ]
63- } )
64-
6573 Sentry.Test . start_collecting ( )
6674
67- assert :ok =
68- ErrorReporter . handle_event (
69- [ :oban , :job , :exception ] ,
70- % { } ,
71- % { job: job } ,
72- :no_config
73- )
75+ emit_telemetry_for_failed_job ( :error , :undef , [ ] )
7476
7577 assert [ event ] = Sentry.Test . pop_sentry_reports ( )
7678 assert % { job: % Oban.Job { } } = event . integration_meta . oban
7779
7880 assert event . message == % Sentry.Interfaces.Message {
79- formatted:
80- "Oban job Sentry.Integrations.Oban.ErrorReporterTest.MyWorker errored out: :undef" ,
81- message:
82- "Oban job Sentry.Integrations.Oban.ErrorReporterTest.MyWorker errored out: %s" ,
81+ formatted: "Oban job #{ @ worker_as_string } errored out: :undef" ,
82+ message: "Oban job #{ @ worker_as_string } errored out: %s" ,
8383 params: [ ":undef" ]
8484 }
8585
8686 assert [ % Sentry.Interfaces.Thread { stacktrace: % { frames: [ stacktrace ] } } ] = event . threads
87-
8887 assert stacktrace . module == MyWorker
89-
90- assert stacktrace . function ==
91- "Sentry.Integrations.Oban.ErrorReporterTest.MyWorker.process/1"
88+ assert stacktrace . function == "#{ @ worker_as_string } .process/1"
9289
9390 assert event . tags . oban_queue == "default"
9491 assert event . tags . oban_state == "available"
95- assert event . tags . oban_worker == "Sentry.Integrations.Oban.ErrorReporterTest.MyWorker"
92+ assert event . tags . oban_worker == @ worker_as_string
93+
94+ assert event . fingerprint == [ @ worker_as_string , "{{ default }}" ]
9695 end
96+
97+ for reason <- [ :cancel , :discard ] do
98+ test "doesn't report Oban.PerformError with reason #{ inspect ( reason ) } " do
99+ Sentry.Test . start_collecting ( )
100+
101+ emit_telemetry_for_failed_job (
102+ :error ,
103+ % Oban.PerformError { reason: { unquote ( reason ) , "nah" } } ,
104+ [ ]
105+ )
106+
107+ assert Sentry.Test . pop_sentry_reports ( ) == [ ]
108+ end
109+ end
110+ end
111+
112+ ## Helpers
113+
114+ defp emit_telemetry_for_failed_job ( kind , reason , stacktrace ) do
115+ job =
116+ % { "id" => "123" , "entity" => "user" , "type" => "delete" }
117+ |> MyWorker . new ( )
118+ |> Ecto.Changeset . apply_action! ( :validate )
119+ |> Map . replace! ( :unsaved_error , % { kind: kind , reason: reason , stacktrace: stacktrace } )
120+
121+ assert :ok =
122+ ErrorReporter . handle_event (
123+ [ :oban , :job , :exception ] ,
124+ % { } ,
125+ % { job: job } ,
126+ :no_config
127+ )
128+
129+ job
97130 end
98131end
0 commit comments