exception_handling 3.0.pre.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/workflows/pipeline.yml +36 -0
  4. data/.gitignore +3 -0
  5. data/.rspec +3 -0
  6. data/.ruby-version +1 -1
  7. data/.tool-versions +1 -0
  8. data/Appraisals +13 -0
  9. data/CHANGELOG.md +150 -0
  10. data/Gemfile +10 -16
  11. data/Gemfile.lock +65 -128
  12. data/README.md +51 -19
  13. data/Rakefile +8 -11
  14. data/exception_handling.gemspec +11 -13
  15. data/gemfiles/rails_5.gemfile +16 -0
  16. data/gemfiles/rails_6.gemfile +16 -0
  17. data/gemfiles/rails_7.gemfile +16 -0
  18. data/lib/exception_handling/escalate_callback.rb +19 -0
  19. data/lib/exception_handling/exception_info.rb +15 -11
  20. data/lib/exception_handling/log_stub_error.rb +2 -1
  21. data/lib/exception_handling/logging_methods.rb +21 -0
  22. data/lib/exception_handling/testing.rb +9 -12
  23. data/lib/exception_handling/version.rb +1 -1
  24. data/lib/exception_handling.rb +83 -173
  25. data/{test → spec}/helpers/exception_helpers.rb +2 -2
  26. data/spec/rake_test_warning_false.rb +20 -0
  27. data/{test/test_helper.rb → spec/spec_helper.rb} +63 -66
  28. data/spec/unit/exception_handling/escalate_callback_spec.rb +81 -0
  29. data/spec/unit/exception_handling/exception_catalog_spec.rb +85 -0
  30. data/spec/unit/exception_handling/exception_description_spec.rb +82 -0
  31. data/{test/unit/exception_handling/exception_info_test.rb → spec/unit/exception_handling/exception_info_spec.rb} +170 -114
  32. data/{test/unit/exception_handling/log_error_stub_test.rb → spec/unit/exception_handling/log_error_stub_spec.rb} +38 -22
  33. data/spec/unit/exception_handling/logging_methods_spec.rb +38 -0
  34. data/spec/unit/exception_handling_spec.rb +1063 -0
  35. metadata +62 -91
  36. data/lib/exception_handling/honeybadger_callbacks.rb +0 -59
  37. data/lib/exception_handling/mailer.rb +0 -70
  38. data/lib/exception_handling/methods.rb +0 -101
  39. data/lib/exception_handling/sensu.rb +0 -28
  40. data/semaphore_ci/setup.sh +0 -3
  41. data/test/unit/exception_handling/exception_catalog_test.rb +0 -85
  42. data/test/unit/exception_handling/exception_description_test.rb +0 -82
  43. data/test/unit/exception_handling/honeybadger_callbacks_test.rb +0 -122
  44. data/test/unit/exception_handling/mailer_test.rb +0 -98
  45. data/test/unit/exception_handling/methods_test.rb +0 -84
  46. data/test/unit/exception_handling/sensu_test.rb +0 -52
  47. data/test/unit/exception_handling_test.rb +0 -1109
  48. data/views/exception_handling/mailer/escalate_custom.html.erb +0 -17
  49. data/views/exception_handling/mailer/escalation_notification.html.erb +0 -17
  50. data/views/exception_handling/mailer/log_parser_exception_notification.html.erb +0 -82
  51. /data/{test → spec}/helpers/controller_helpers.rb +0 -0
@@ -1,58 +1,75 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require File.expand_path('../../test_helper', __dir__)
3
+ require File.expand_path('../../spec_helper', __dir__)
4
4
  require_test_helper 'controller_helpers'
5
5
  require_test_helper 'exception_helpers'
6
6
 
7
7
  module ExceptionHandling
8
- class ExceptionInfoTest < ActiveSupport::TestCase
8
+ describe ExceptionInfo do
9
9
  include ControllerHelpers
10
10
  include ExceptionHelpers
11
11
 
12
12
  context "initialize" do
13
- setup do
13
+ before do
14
14
  @exception = StandardError.new("something went wrong")
15
15
  @timestamp = Time.now
16
16
  @controller = Object.new
17
17
  end
18
18
 
19
19
  context "controller_from_context" do
20
- should "extract controller from context when not specified explicitly" do
20
+ it "extract controller from context when not specified explicitly" do
21
21
  exception_context = {
22
22
  "action_controller.instance" => @controller
23
23
  }
24
24
  exception_info = ExceptionInfo.new(@exception, exception_context, @timestamp)
25
- assert_equal @controller, exception_info.controller
25
+ expect(exception_info.controller).to eq(@controller)
26
26
  end
27
27
 
28
- should "prefer the explicit controller over the one from context" do
28
+ it "prefer the explicit controller over the one from context" do
29
29
  exception_context = {
30
30
  "action_controller.instance" => Object.new
31
31
  }
32
- exception_info = ExceptionInfo.new(@exception, exception_context, @timestamp, @controller)
33
- assert_equal @controller, exception_info.controller
34
- assert_not_equal exception_context["action_controller.instance"], exception_info.controller
32
+ exception_info = ExceptionInfo.new(@exception, exception_context, @timestamp, controller: @controller)
33
+ expect(exception_info.controller).to eq(@controller)
34
+ expect(exception_info.controller).not_to eq(exception_context["action_controller.instance"])
35
35
  end
36
36
 
37
- should "leave controller unset when not included in the context hash" do
37
+ it "leave controller unset when not included in the context hash" do
38
38
  exception_info = ExceptionInfo.new(@exception, {}, @timestamp)
39
- assert_nil exception_info.controller
39
+ expect(exception_info.controller).to be_nil
40
40
  end
41
41
 
42
- should "leave controller unset when context is not in hash format" do
42
+ it "leave controller unset when context is not in hash format" do
43
43
  exception_info = ExceptionInfo.new(@exception, "string context", @timestamp)
44
- assert_nil exception_info.controller
44
+ expect(exception_info.controller).to be_nil
45
+ end
46
+ end
47
+
48
+ context "honeybadger_tags" do
49
+ it "retrieved from log_context" do
50
+ exception_info = ExceptionInfo.new(@exception, "string context", @timestamp, log_context: { honeybadger_tags: ['Data-Services'] })
51
+ expect(exception_info.honeybadger_tags).to eq(['Data-Services'])
52
+ end
53
+
54
+ it "retrieved from log_context and normalized to array if a string" do
55
+ exception_info = ExceptionInfo.new(@exception, "string context", @timestamp, log_context: { honeybadger_tags: 'Data-Services' })
56
+ expect(exception_info.honeybadger_tags).to eq(['Data-Services'])
57
+ end
58
+
59
+ it "defaults to empty array if not given" do
60
+ exception_info = ExceptionInfo.new(@exception, "string context", @timestamp)
61
+ expect(exception_info.honeybadger_tags).to eq([])
45
62
  end
46
63
  end
47
64
  end
48
65
 
49
66
  context "data" do
50
- setup do
67
+ before do
51
68
  @exception = StandardError.new("something went wrong")
52
69
  @timestamp = Time.now
53
70
  end
54
71
 
55
- should "return a hash with exception specific data including context hash" do
72
+ it "return a hash with exception specific data including context hash" do
56
73
  exception_context = {
57
74
  "rack.session" => {
58
75
  user_id: 23,
@@ -73,17 +90,17 @@ module ExceptionHandling
73
90
  }
74
91
  }
75
92
 
76
- assert_equal_with_diff expected_data, exception_info.data
93
+ expect(exception_info.data).to eq(expected_data)
77
94
  end
78
95
 
79
- should "generate exception data appropriately if exception message is nil" do
96
+ it "generate exception data appropriately if exception message is nil" do
80
97
  exception_info = ExceptionInfo.new(exception_with_nil_message, "custom context data", @timestamp)
81
98
  exception_data = exception_info.data
82
- assert_equal "RuntimeError: ", exception_data["error_string"]
83
- assert_equal "RuntimeError: : custom context data", exception_data["error"]
99
+ expect(exception_data["error_string"]).to eq("RuntimeError: ")
100
+ expect(exception_data["error"]).to eq("RuntimeError: : custom context data")
84
101
  end
85
102
 
86
- should "return a hash with exception specific data including context string" do
103
+ it "return a hash with exception specific data including context string" do
87
104
  exception_context = "custom context data"
88
105
  exception_info = ExceptionInfo.new(@exception, exception_context, @timestamp)
89
106
  expected_data = {
@@ -96,21 +113,20 @@ module ExceptionHandling
96
113
  "message" => "custom context data"
97
114
  }
98
115
  }
99
-
100
- assert_equal_with_diff expected_data, exception_info.data
116
+ expect(exception_info.data).to eq(expected_data)
101
117
  end
102
118
 
103
- should "not include enhanced data from controller or custom data callback" do
119
+ it "not include enhanced data from controller or custom data callback" do
104
120
  env = { server: "fe98" }
105
121
  parameters = { advertiser_id: 435 }
106
122
  session = { username: "jsmith" }
107
123
  request_uri = "host/path"
108
124
  controller = create_dummy_controller(env, parameters, session, request_uri)
109
125
  data_callback = ->(data) { data[:custom_section] = "value" }
110
- exception_info = ExceptionInfo.new(@exception, "custom context data", @timestamp, controller, data_callback)
126
+ exception_info = ExceptionInfo.new(@exception, "custom context data", @timestamp, controller: controller, data_callback: data_callback)
111
127
 
112
- dont_allow(exception_info).extract_and_merge_controller_data
113
- dont_allow(exception_info).customize_from_data_callback
128
+ expect(exception_info).to_not receive(:extract_and_merge_controller_data)
129
+ expect(exception_info).to_not receive(:customize_from_data_callback)
114
130
  expected_data = {
115
131
  "error_class" => "StandardError",
116
132
  "error_string" => "StandardError: something went wrong",
@@ -122,12 +138,12 @@ module ExceptionHandling
122
138
  }
123
139
  }
124
140
 
125
- assert_equal_with_diff expected_data, exception_info.data
141
+ expect(exception_info.data).to eq(expected_data)
126
142
  end
127
143
  end
128
144
 
129
145
  context "enhanced_data" do
130
- setup do
146
+ before do
131
147
  @exception = StandardError.new("something went wrong")
132
148
  @timestamp = Time.now
133
149
  @exception_context = {
@@ -145,13 +161,13 @@ module ExceptionHandling
145
161
  @data_callback = ->(data) { data[:custom_section] = "check this out" }
146
162
  end
147
163
 
148
- should "not return a mutable object for the session" do
164
+ it "not return a mutable object for the session" do
149
165
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
150
166
  exception_info.enhanced_data["session"]["hello"] = "world"
151
- assert_nil @controller.session["hello"]
167
+ expect(@controller.session["hello"]).to be_nil
152
168
  end
153
169
 
154
- should "return a hash with generic exception attributes as well as context data" do
170
+ it "return a hash with generic exception attributes as well as context data" do
155
171
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
156
172
  expected_data = {
157
173
  "error_class" => "StandardError",
@@ -164,20 +180,20 @@ module ExceptionHandling
164
180
  "location" => { "file" => "<no backtrace>", "line" => nil }
165
181
  }
166
182
 
167
- assert_equal_with_diff expected_data, prepare_data(exception_info.enhanced_data)
183
+ expect(prepare_data(exception_info.enhanced_data)).to eq(expected_data)
168
184
  end
169
185
 
170
- should "generate exception data appropriately if exception message is nil" do
186
+ it "generate exception data appropriately if exception message is nil" do
171
187
  exception_with_nil_message = RuntimeError.new(nil)
172
- stub(exception_with_nil_message).message { nil }
188
+ allow(exception_with_nil_message).to receive(:message).and_return(nil)
173
189
  exception_info = ExceptionInfo.new(exception_with_nil_message, @exception_context, @timestamp)
174
190
  exception_data = exception_info.enhanced_data
175
- assert_equal "RuntimeError: ", exception_data["error_string"]
176
- assert_equal "RuntimeError: ", exception_data["error"]
191
+ expect(exception_data["error_string"]).to eq("RuntimeError: ")
192
+ expect(exception_data["error"]).to eq("RuntimeError: ")
177
193
  end
178
194
 
179
- should "include controller data when available" do
180
- exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, @controller)
195
+ it "include controller data when available" do
196
+ exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: @controller)
181
197
  expected_data = {
182
198
  "error_class" => "StandardError",
183
199
  "error_string" => "StandardError: something went wrong",
@@ -194,10 +210,10 @@ module ExceptionHandling
194
210
  "location" => { "controller" => "dummy", "action" => "fail", "file" => "<no backtrace>", "line" => nil }
195
211
  }
196
212
 
197
- assert_equal_with_diff expected_data, prepare_data(exception_info.enhanced_data)
213
+ expect(prepare_data(exception_info.enhanced_data)).to eq(expected_data)
198
214
  end
199
215
 
200
- should "extract controller from rack specific exception context when not provided explicitly" do
216
+ it "extract controller from rack specific exception context when not provided explicitly" do
201
217
  @exception_context["action_controller.instance"] = @controller
202
218
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
203
219
  expected_data = {
@@ -216,11 +232,11 @@ module ExceptionHandling
216
232
  "location" => { "controller" => "dummy", "action" => "fail", "file" => "<no backtrace>", "line" => nil }
217
233
  }
218
234
 
219
- assert_equal_with_diff expected_data, prepare_data(exception_info.enhanced_data)
235
+ expect(prepare_data(exception_info.enhanced_data)).to eq(expected_data)
220
236
  end
221
237
 
222
- should "add to_s attribute to specific sections that have their content in hash format" do
223
- exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, @controller)
238
+ it "add to_s attribute to specific sections that have their content in hash format" do
239
+ exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: @controller)
224
240
  expected_data = {
225
241
  "error_class" => "StandardError",
226
242
  "error_string" => "StandardError: something went wrong",
@@ -245,13 +261,13 @@ module ExceptionHandling
245
261
  "location" => { "controller" => "dummy", "action" => "fail", "file" => "<no backtrace>", "line" => nil }
246
262
  }
247
263
 
248
- assert_equal_with_diff expected_data, exception_info.enhanced_data
264
+ expect(exception_info.enhanced_data).to eq(expected_data)
249
265
  end
250
266
 
251
- should "filter out sensitive parameters like passwords" do
267
+ it "filter out sensitive parameters like passwords" do
252
268
  @controller.request.parameters[:password] = "super_secret"
253
269
  @controller.request.parameters[:user] = { "password" => "also super secret", "password_confirmation" => "also super secret" }
254
- exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, @controller)
270
+ exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: @controller)
255
271
  expected_params = {
256
272
  "password" => "[FILTERED]",
257
273
  "advertiser_id" => 435, "controller" => "dummy",
@@ -261,11 +277,11 @@ module ExceptionHandling
261
277
  "password_confirmation" => "[FILTERED]"
262
278
  }
263
279
  }
264
- assert_equal_with_diff expected_params, exception_info.enhanced_data["request"]["params"]
280
+ expect(exception_info.enhanced_data["request"]["params"]).to eq(expected_params)
265
281
  end
266
282
 
267
- should "include the changes from the custom data callback" do
268
- exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, nil, @data_callback)
283
+ it "include the changes from the custom data callback" do
284
+ exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: nil, data_callback: @data_callback)
269
285
  expected_data = {
270
286
  "error_class" => "StandardError",
271
287
  "error_string" => "StandardError: something went wrong",
@@ -278,11 +294,11 @@ module ExceptionHandling
278
294
  "location" => { "file" => "<no backtrace>", "line" => nil }
279
295
  }
280
296
 
281
- assert_equal_with_diff expected_data, prepare_data(exception_info.enhanced_data)
297
+ expect(prepare_data(exception_info.enhanced_data)).to eq(expected_data)
282
298
  end
283
299
 
284
- should "apply the custom_data_hook results" do
285
- stub(ExceptionHandling).custom_data_hook { ->(data) { data[:custom_hook] = "changes from custom hook" } }
300
+ it "apply the custom_data_hook results" do
301
+ allow(ExceptionHandling).to receive(:custom_data_hook).and_return(->(data) { data[:custom_hook] = "changes from custom hook" })
286
302
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
287
303
  expected_data = {
288
304
  "error_class" => "StandardError",
@@ -296,61 +312,61 @@ module ExceptionHandling
296
312
  "location" => { "file" => "<no backtrace>", "line" => nil }
297
313
  }
298
314
 
299
- assert_equal_with_diff expected_data, prepare_data(exception_info.enhanced_data)
315
+ expect(prepare_data(exception_info.enhanced_data)).to eq(expected_data)
300
316
  end
301
317
 
302
- should "log info if the custom data hook results in a nil message exception" do
318
+ it "log info if the custom data hook results in a nil message exception" do
303
319
  ExceptionHandling.custom_data_hook = ->(_data) do
304
320
  raise_exception_with_nil_message
305
321
  end
306
322
  log_info_messages = []
307
- stub(ExceptionHandling.logger).info.with_any_args do |message, _|
323
+ allow(ExceptionHandling.logger).to receive(:info).with(any_args) do |message, _|
308
324
  log_info_messages << message
309
325
  end
310
326
 
311
327
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
312
328
  exception_info.enhanced_data
313
- assert log_info_messages.find { |message| message =~ /Unable to execute custom custom_data_hook callback/ }
329
+ expect(log_info_messages.find { |message| message =~ /Unable to execute custom custom_data_hook callback/ }).to be_truthy
314
330
  ExceptionHandling.custom_data_hook = nil
315
331
  end
316
332
  end
317
333
 
318
334
  context "exception_description" do
319
- should "return the exception description from the global exception filter list" do
335
+ it "return the exception description from the global exception filter list" do
320
336
  exception = StandardError.new("No route matches")
321
337
  exception_info = ExceptionInfo.new(exception, {}, Time.now)
322
338
  description = exception_info.exception_description
323
- assert_not_nil description
324
- assert_equal :NoRoute, description.filter_name
339
+ expect(description).to_not be_nil
340
+ expect(description.filter_name).to eq(:NoRoute)
325
341
  end
326
342
 
327
- should "find the description when filter criteria includes section in hash format" do
343
+ it "find the description when filter criteria includes section in hash format" do
328
344
  env = { server: "fe98" }
329
345
  parameters = { advertiser_id: 435, controller: "sessions", action: "fail" }
330
346
  session = { username: "jsmith", id: "session_key" }
331
347
  request_uri = "host/path"
332
348
  controller = create_dummy_controller(env, parameters, session, request_uri)
333
349
  exception = StandardError.new("Request to click domain rejected")
334
- exception_info = ExceptionInfo.new(exception, {}, Time.now, controller)
335
- assert_equal true, exception_info.enhanced_data[:request].is_a?(Hash)
350
+ exception_info = ExceptionInfo.new(exception, nil, Time.now, controller: controller)
351
+ expect(exception_info.enhanced_data[:request].is_a?(Hash)).to eq(true)
336
352
  description = exception_info.exception_description
337
- assert_not_nil description
338
- assert_equal :"Click Request Rejected", description.filter_name
353
+ expect(description).to_not be_nil
354
+ expect(description.filter_name).to eq(:"Click Request Rejected")
339
355
  end
340
356
 
341
- should "return same description object for related errors (avoid reloading exception catalog from disk)" do
357
+ it "return same description object for related errors (avoid reloading exception catalog from disk)" do
342
358
  exception = StandardError.new("No route matches")
343
- exception_info = ExceptionInfo.new(exception, {}, Time.now)
359
+ exception_info = ExceptionInfo.new(exception, nil, Time.now)
344
360
  description = exception_info.exception_description
345
361
 
346
362
  repeat_ex = StandardError.new("No route matches 2")
347
- repeat_ex_info = ExceptionInfo.new(repeat_ex, {}, Time.now)
348
- assert_equal description.object_id, repeat_ex_info.exception_description.object_id
363
+ repeat_ex_info = ExceptionInfo.new(repeat_ex, nil, Time.now)
364
+ expect(repeat_ex_info.exception_description.object_id).to eq(description.object_id)
349
365
  end
350
366
  end
351
367
 
352
368
  context "controller_name" do
353
- setup do
369
+ before do
354
370
  @exception = StandardError.new('something went wrong')
355
371
  @timestamp = Time.now
356
372
  @exception_context = {
@@ -362,73 +378,113 @@ module ExceptionHandling
362
378
  }
363
379
  end
364
380
 
365
- should "return controller_name when controller is present" do
381
+ it "returns honeybadger_grouping from the log context" do
382
+ allow(ExceptionHandling.logger).to receive(:current_context_for_thread).and_return({ honeybadger_grouping: 'Group 1' })
383
+ exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
384
+
385
+ expect(exception_info.controller_name).to eq('Group 1')
386
+ end
387
+
388
+ it "returns honeybadger_grouping from log context even if controller is defined" do
389
+ allow(ExceptionHandling.logger).to receive(:current_context_for_thread).and_return({ honeybadger_grouping: 'Group 1' })
390
+
366
391
  env = { server: 'fe98' }
367
392
  parameters = { controller: 'some_controller' }
368
393
  session = { username: 'smith' }
369
394
  request_uri = "host/path"
370
395
  controller = create_dummy_controller(env, parameters, session, request_uri)
371
- exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller)
396
+ exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: controller)
372
397
 
373
- assert_equal 'some_controller', exception_info.controller_name
398
+ expect(exception_info.controller_name).to eq('Group 1')
374
399
  end
375
400
 
376
- should "return an empty string when controller is not present" do
401
+ it "return controller_name when controller is present" do
402
+ env = { server: 'fe98' }
403
+ parameters = { controller: 'some_controller' }
404
+ session = { username: 'smith' }
405
+ request_uri = "host/path"
406
+ controller = create_dummy_controller(env, parameters, session, request_uri)
407
+ exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp, controller: controller)
408
+
409
+ expect(exception_info.controller_name).to eq('some_controller')
410
+ end
411
+
412
+ it "return an empty string when controller is not present" do
377
413
  exception_info = ExceptionInfo.new(@exception, @exception_context, @timestamp)
378
414
 
379
- assert_equal '', exception_info.controller_name
415
+ expect(exception_info.controller_name).to eq('')
380
416
  end
381
417
  end
382
418
 
383
419
  context "send_to_honeybadger?" do
384
- should "be enabled when Honeybadger is defined and exception is not in the filter list" do
385
- stub(ExceptionHandling).honeybadger_defined? { true }
420
+ it "be enabled when Honeybadger is defined and exception is not in the filter list" do
421
+ allow(ExceptionHandling).to receive(:honeybadger_defined?).and_return(true)
386
422
  exception = StandardError.new("something went wrong")
387
- exception_info = ExceptionInfo.new(exception, {}, Time.now)
388
- assert_nil exception_info.exception_description
389
- assert_equal true, exception_info.send_to_honeybadger?
423
+ exception_info = ExceptionInfo.new(exception, nil, Time.now)
424
+ expect(exception_info.exception_description).to be_nil
425
+ expect(exception_info.send_to_honeybadger?).to eq(true)
390
426
  end
391
427
 
392
- should "be enabled when Honeybadger is defined and exception is on the filter list with the flag turned on" do
393
- stub(ExceptionHandling).honeybadger_defined? { true }
428
+ it "be enabled when Honeybadger is defined and exception is on the filter list with the flag turned on" do
429
+ allow(ExceptionHandling).to receive(:honeybadger_defined?).and_return(true)
394
430
  exception = StandardError.new("No route matches")
395
- exception_info = ExceptionInfo.new(exception, {}, Time.now)
396
- assert_not_nil exception_info.exception_description
397
- assert_equal true, exception_info.exception_description.send_to_honeybadger
398
- assert_equal true, exception_info.send_to_honeybadger?
431
+ exception_info = ExceptionInfo.new(exception, nil, Time.now)
432
+ expect(exception_info.exception_description).to_not be_nil
433
+ expect(exception_info.exception_description.send_to_honeybadger).to eq(true)
434
+ expect(exception_info.send_to_honeybadger?).to eq(true)
399
435
  end
400
436
 
401
- should "be disabled when Honeybadger is defined and exception is on the filter list with the flag turned off" do
402
- stub(ExceptionHandling).honeybadger_defined? { true }
437
+ it "be disabled when Honeybadger is defined and exception is on the filter list with the flag turned off" do
438
+ allow(ExceptionHandling).to receive(:honeybadger_defined?).and_return(true)
403
439
  exception = StandardError.new("No route matches")
404
- exception_info = ExceptionInfo.new(exception, {}, Time.now)
405
- assert_not_nil exception_info.exception_description
406
- stub(exception_info.exception_description).send_to_honeybadger { false }
407
- assert_equal false, exception_info.send_to_honeybadger?
440
+ exception_info = ExceptionInfo.new(exception, nil, Time.now)
441
+ expect(exception_info.exception_description).to_not be_nil
442
+ allow(exception_info.exception_description).to receive(:send_to_honeybadger).and_return(false)
443
+ expect(exception_info.send_to_honeybadger?).to eq(false)
408
444
  end
409
445
 
410
- should "be disabled when Honeybadger is not defined" do
411
- stub(ExceptionHandling).honeybadger_defined? { false }
446
+ it "be disabled when Honeybadger is not defined" do
447
+ allow(ExceptionHandling).to receive(:honeybadger_defined?).and_return(false)
412
448
  exception = StandardError.new("something went wrong")
413
- exception_info = ExceptionInfo.new(exception, {}, Time.now)
414
- assert_nil exception_info.exception_description
415
- assert_equal false, exception_info.send_to_honeybadger?
449
+ exception_info = ExceptionInfo.new(exception, nil, Time.now)
450
+ expect(exception_info.exception_description).to be_nil
451
+ expect(exception_info.send_to_honeybadger?).to eq(false)
416
452
  end
417
453
  end
418
454
 
419
455
  context "honeybadger_context_data" do
420
- should "return the error details and relevant context data to be used as honeybadger notification context while filtering sensitive data" do
456
+ before do
457
+ allow(ExceptionHandling.logger).to receive(:current_context_for_thread).and_return({ cuid: 'ABCD' })
458
+ end
459
+
460
+ it "include thread_context when log_context: is nil" do
461
+ exception_with_nil_message = RuntimeError.new(nil)
462
+ allow(exception_with_nil_message).to receive(:message).and_return(nil)
463
+ exception_info = ExceptionInfo.new(exception_with_nil_message, @exception_context, @timestamp)
464
+ honeybadger_context_data = exception_info.honeybadger_context_data
465
+ expect(honeybadger_context_data[:log_context]).to eq({ "cuid" => 'ABCD' })
466
+ end
467
+
468
+ it "include thread context merged with log_context:" do
469
+ exception_with_nil_message = RuntimeError.new(nil)
470
+ allow(exception_with_nil_message).to receive(:message).and_return(nil)
471
+ exception_info = ExceptionInfo.new(exception_with_nil_message, @exception_context, @timestamp, log_context: { url: 'http://example.com' })
472
+ honeybadger_context_data = exception_info.honeybadger_context_data
473
+ expect(honeybadger_context_data[:log_context]).to eq({ "cuid" => 'ABCD', "url" => 'http://example.com' })
474
+ end
475
+
476
+ it "return the error details and relevant context data to be used as honeybadger notification context while filtering sensitive data" do
421
477
  env = { server: "fe98" }
422
478
  parameters = { advertiser_id: 435 }
423
479
  session = { username: "jsmith" }
424
480
  request_uri = "host/path"
425
481
  controller = create_dummy_controller(env, parameters, session, request_uri)
426
- stub(ExceptionHandling).server_name { "invoca_fe98" }
482
+ allow(ExceptionHandling).to receive(:server_name).and_return("invoca_fe98")
427
483
 
428
484
  exception = StandardError.new("Some Exception")
429
485
  exception.set_backtrace([
430
- "test/unit/exception_handling_test.rb:847:in `exception_1'",
431
- "test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
486
+ "spec/unit/exception_handling_test.rb:847:in `exception_1'",
487
+ "spec/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
432
488
  ])
433
489
  exception_context = { "SERVER_NAME" => "exceptional.com" }
434
490
  data_callback = ->(data) do
@@ -438,7 +494,7 @@ module ExceptionHandling
438
494
  data[:other_section] = "This should not be included in the response"
439
495
  end
440
496
  timestamp = Time.now
441
- exception_info = ExceptionInfo.new(exception, exception_context, timestamp, controller, data_callback)
497
+ exception_info = ExceptionInfo.new(exception, exception_context, timestamp, controller: controller, data_callback: data_callback)
442
498
 
443
499
  expected_data = {
444
500
  timestamp: timestamp,
@@ -446,7 +502,6 @@ module ExceptionHandling
446
502
  exception_context: { "SERVER_NAME" => "exceptional.com" },
447
503
  server: "invoca_fe98",
448
504
  scm_revision: "5b24eac37aaa91f5784901e9aabcead36fd9df82",
449
- notes: "this is used by a test",
450
505
  user_details: { "username" => "jsmith" },
451
506
  request: {
452
507
  "params" => { "advertiser_id" => 435 },
@@ -461,28 +516,30 @@ module ExceptionHandling
461
516
  "SERVER_NAME" => "exceptional.com"
462
517
  },
463
518
  backtrace: [
464
- "test/unit/exception_handling_test.rb:847:in `exception_1'",
465
- "test/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
519
+ "spec/unit/exception_handling_test.rb:847:in `exception_1'",
520
+ "spec/unit/exception_handling_test.rb:455:in `block (4 levels) in <class:ExceptionHandlingTest>'"
466
521
  ],
467
- event_response: "Event successfully received"
522
+ event_response: "Event successfully received",
523
+ log_context: { "cuid" => "ABCD" },
524
+ notes: "this is used by a test"
468
525
  }
469
- assert_equal_with_diff expected_data, exception_info.honeybadger_context_data
526
+ expect(exception_info.honeybadger_context_data).to eq(expected_data)
470
527
  end
471
528
 
472
529
  [['Hash', { 'cookie' => 'cookie_context' }],
473
530
  ['String', 'Entering Error State'],
474
531
  ['Array', ['Error1', 'Error2']]].each do |klass, value|
475
- should "extract context from exception_context when it is a #{klass}" do
532
+ it "extract context from exception_context when it is a #{klass}" do
476
533
  exception = StandardError.new("Exception")
477
534
  exception_context = value
478
535
  exception_info = ExceptionInfo.new(exception, exception_context, Time.now)
479
536
 
480
- assert_equal klass, value.class.name
481
- assert_equal value, exception_info.honeybadger_context_data[:exception_context]
537
+ expect(value.class.name).to eq(klass)
538
+ expect(exception_info.honeybadger_context_data[:exception_context]).to eq(value)
482
539
  end
483
540
  end
484
541
 
485
- should "filter out sensitive data from exception context such as [password, password_confirmation, oauth_token]" do
542
+ it "filter out sensitive data from exception context such as [password, password_confirmation, oauth_token]" do
486
543
  sensitive_data = {
487
544
  "password" => "super_secret",
488
545
  "password_confirmation" => "super_secret_confirmation",
@@ -517,15 +574,14 @@ module ExceptionHandling
517
574
  }
518
575
  }.merge(expected_sensitive_data)
519
576
 
520
- assert_equal_with_diff expected_exception_context, exception_info.honeybadger_context_data[:exception_context]
577
+ expect(exception_info.honeybadger_context_data[:exception_context]).to eq(expected_exception_context)
521
578
  end
522
579
 
523
- should "omit context if exception_context is empty" do
580
+ it "omit context if exception_context is empty" do
524
581
  exception = StandardError.new("Exception")
525
582
  exception_context = ""
526
583
  exception_info = ExceptionInfo.new(exception, exception_context, Time.now)
527
-
528
- assert_nil exception_info.honeybadger_context_data[:exception_context]
584
+ expect(exception_info.honeybadger_context_data[:exception_context]).to be_nil
529
585
  end
530
586
  end
531
587