bugsnag 6.13.1 → 6.14.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.
@@ -332,10 +332,14 @@ describe Bugsnag::Configuration do
332
332
  end
333
333
  end
334
334
 
335
- it "should have exit exception classes ignored by default" do
335
+ it "should have exit exception classes in ignore_classes by default" do
336
336
  expect(subject.ignore_classes).to eq(Set.new([SystemExit, SignalException]))
337
337
  end
338
338
 
339
+ it "should have nothing in discard_classes by default" do
340
+ expect(subject.discard_classes).to eq(Set.new([]))
341
+ end
342
+
339
343
  describe "#breadcrumbs" do
340
344
  it "first returns a new circular buffer" do
341
345
  buffer = subject.breadcrumbs
@@ -4,23 +4,7 @@ require 'spec_helper'
4
4
  require 'set'
5
5
 
6
6
  describe Bugsnag::Helpers do
7
-
8
7
  describe "trim_if_needed" do
9
-
10
- it "breaks recursion" do
11
- a = [1, 2, 3]
12
- b = [2, a]
13
- a << b
14
- value = Bugsnag::Helpers.trim_if_needed(a)
15
- expect(value).to eq([1, 2, 3, [2, "[RECURSION]"]])
16
- end
17
-
18
- it "does not break equal objects without recursion" do
19
- data = [1, [1, 2], [1, 2], "a"]
20
- value = Bugsnag::Helpers.trim_if_needed(data)
21
- expect(value).to eq data
22
- end
23
-
24
8
  it "preserves bool types" do
25
9
  value = Bugsnag::Helpers.trim_if_needed([1, 3, true, "NO", "2", false])
26
10
  expect(value[2]).to be_a(TrueClass)
@@ -39,76 +23,7 @@ describe Bugsnag::Helpers do
39
23
  expect(value[4]).to be_a(String)
40
24
  end
41
25
 
42
- context "an object will throw if `to_s` is called" do
43
- class StringRaiser
44
- def to_s
45
- raise 'Oh no you do not!'
46
- end
47
- end
48
-
49
- it "uses the string '[RAISED]' instead" do
50
- value = Bugsnag::Helpers.trim_if_needed([1, 3, StringRaiser.new])
51
- expect(value[2]).to eq "[RAISED]"
52
- end
53
-
54
- it "replaces hash key with '[RAISED]'" do
55
- a = {}
56
- a[StringRaiser.new] = 1
57
-
58
- value = Bugsnag::Helpers.trim_if_needed(a)
59
- expect(value).to eq({ "[RAISED]" => "[FILTERED]" })
60
- end
61
-
62
- it "uses a single '[RAISED]'key when multiple keys raise" do
63
- a = {}
64
- a[StringRaiser.new] = 1
65
- a[StringRaiser.new] = 2
66
-
67
- value = Bugsnag::Helpers.trim_if_needed(a)
68
- expect(value).to eq({ "[RAISED]" => "[FILTERED]" })
69
- end
70
- end
71
-
72
- context "an object will infinitely recurse if `to_s` is called" do
73
- is_jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
74
-
75
- class StringRecurser
76
- def to_s
77
- to_s
78
- end
79
- end
80
-
81
- it "uses the string '[RECURSION]' instead" do
82
- skip "JRuby doesn't allow recovery from SystemStackErrors" if is_jruby
83
-
84
- value = Bugsnag::Helpers.trim_if_needed([1, 3, StringRecurser.new])
85
- expect(value[2]).to eq "[RECURSION]"
86
- end
87
-
88
- it "replaces hash key with '[RECURSION]'" do
89
- skip "JRuby doesn't allow recovery from SystemStackErrors" if is_jruby
90
-
91
- a = {}
92
- a[StringRecurser.new] = 1
93
-
94
- value = Bugsnag::Helpers.trim_if_needed(a)
95
- expect(value).to eq({ "[RECURSION]" => "[FILTERED]" })
96
- end
97
-
98
- it "uses a single '[RECURSION]'key when multiple keys recurse" do
99
- skip "JRuby doesn't allow recovery from SystemStackErrors" if is_jruby
100
-
101
- a = {}
102
- a[StringRecurser.new] = 1
103
- a[StringRecurser.new] = 2
104
-
105
- value = Bugsnag::Helpers.trim_if_needed(a)
106
- expect(value).to eq({ "[RECURSION]" => "[FILTERED]" })
107
- end
108
- end
109
-
110
26
  context "payload length is less than allowed" do
111
-
112
27
  it "does not change strings" do
113
28
  value = SecureRandom.hex(4096)
114
29
  expect(Bugsnag::Helpers.trim_if_needed(value)).to eq value
@@ -126,7 +41,6 @@ describe Bugsnag::Helpers do
126
41
  end
127
42
 
128
43
  context "payload length is greater than allowed" do
129
-
130
44
  it "trims metadata strings" do
131
45
  payload = {
132
46
  :events => [{
@@ -127,9 +127,10 @@ describe Bugsnag::Rack do
127
127
  expect(report).to receive(:context=).with("TEST /TEST_PATH")
128
128
  expect(report).to receive(:user).and_return({})
129
129
 
130
- config = double
131
- allow(config).to receive(:send_environment).and_return(true)
132
- allow(config).to receive(:meta_data_filters).and_return(['email'])
130
+ config = Bugsnag.configuration
131
+ config.send_environment = true
132
+ config.meta_data_filters = ['email']
133
+
133
134
  allow(report).to receive(:configuration).and_return(config)
134
135
  expect(report).to receive(:add_tab).once.with(:request, {
135
136
  :url => "http://test_host/TEST_PATH?email=[FILTERED]&another_param=thing",
@@ -187,9 +188,10 @@ describe Bugsnag::Rack do
187
188
  expect(report).to receive(:context=).with("TEST /TEST_PATH")
188
189
  expect(report).to receive(:user).and_return({})
189
190
 
190
- config = double
191
- allow(config).to receive(:send_environment).and_return(true)
192
- allow(config).to receive(:meta_data_filters).and_return(nil)
191
+ config = Bugsnag.configuration
192
+ config.send_environment = true
193
+ config.meta_data_filters = []
194
+
193
195
  allow(report).to receive(:configuration).and_return(config)
194
196
  expect(report).to receive(:add_tab).once.with(:environment, rack_env)
195
197
  expect(report).to receive(:add_tab).once.with(:request, {
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- require 'spec_helper'
2
+ require_relative './spec_helper'
3
3
  require 'securerandom'
4
4
  require 'ostruct'
5
5
 
@@ -27,6 +27,7 @@ class JRubyException
27
27
  end
28
28
  end
29
29
 
30
+ # rubocop:disable Metrics/BlockLength
30
31
  describe Bugsnag::Report do
31
32
  it "should contain an api_key if one is set" do
32
33
  Bugsnag.notify(BugsnagTestException.new("It crashed"))
@@ -149,10 +150,12 @@ describe Bugsnag::Report do
149
150
  original_ignore_classes = Bugsnag.configuration.ignore_classes
150
151
 
151
152
  begin
152
- # The default ignore_classes includes SignalException, so we need to
153
+ # The default ignore classes includes SignalException, so we need to
153
154
  # temporarily set it to something else.
154
155
  Bugsnag.configuration.ignore_classes = Set[SystemExit]
156
+
155
157
  Bugsnag.notify(SignalException.new("TERM"))
158
+
156
159
  expect(Bugsnag).to have_sent_notification{ |payload, headers|
157
160
  event = get_event_from_payload(payload)
158
161
  expect(event["unhandled"]).to be false
@@ -589,7 +592,6 @@ describe Bugsnag::Report do
589
592
  end
590
593
 
591
594
  it "filters params from all payload hashes if they are set in default meta_data_filters" do
592
-
593
595
  Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
594
596
  report.meta_data.merge!({
595
597
  :request => {
@@ -635,7 +637,6 @@ describe Bugsnag::Report do
635
637
  end
636
638
 
637
639
  it "filters params from all payload hashes if they are added to meta_data_filters" do
638
-
639
640
  Bugsnag.configuration.meta_data_filters << "other_data"
640
641
  Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
641
642
  report.meta_data.merge!({:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
@@ -653,7 +654,6 @@ describe Bugsnag::Report do
653
654
  end
654
655
 
655
656
  it "filters params from all payload hashes if they are added to meta_data_filters as regex" do
656
-
657
657
  Bugsnag.configuration.meta_data_filters << /other_data/
658
658
  Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
659
659
  report.meta_data.merge!({:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
@@ -671,7 +671,6 @@ describe Bugsnag::Report do
671
671
  end
672
672
 
673
673
  it "filters params from all payload hashes if they are added to meta_data_filters as partial regex" do
674
-
675
674
  Bugsnag.configuration.meta_data_filters << /r_data/
676
675
  Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
677
676
  report.meta_data.merge!({:request => {:params => {:password => "1234", :other_password => "123456", :other_data => "123456"}}})
@@ -702,6 +701,33 @@ describe Bugsnag::Report do
702
701
  }
703
702
  end
704
703
 
704
+ it "does not apply filters outside of report.meta_data" do
705
+ Bugsnag.configuration.meta_data_filters << "data"
706
+
707
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
708
+ report.meta_data = {
709
+ xyz: "abc",
710
+ data: "123456"
711
+ }
712
+
713
+ report.user = {
714
+ id: 123,
715
+ data: "hello"
716
+ }
717
+ end
718
+
719
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
720
+ event = get_event_from_payload(payload)
721
+
722
+ expect(event["metaData"]).not_to be_nil
723
+ expect(event["metaData"]["xyz"]).to eq("abc")
724
+ expect(event["metaData"]["data"]).to eq("[FILTERED]")
725
+
726
+ expect(event["user"]).not_to be_nil
727
+ expect(event["user"]["data"]).to eq("hello")
728
+ }
729
+ end
730
+
705
731
  it "does not notify if report ignored" do
706
732
  Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
707
733
  report.ignore!
@@ -710,38 +736,154 @@ describe Bugsnag::Report do
710
736
  expect(Bugsnag).not_to have_sent_notification
711
737
  end
712
738
 
713
- it "does not notify if the exception class is in the default ignore_classes list" do
714
- Bugsnag.configuration.ignore_classes << ActiveRecord::RecordNotFound
715
- Bugsnag.notify(ActiveRecord::RecordNotFound.new("It crashed"))
739
+ context "ignore_classes" do
740
+ context "as a constant" do
741
+ it "ignores exception when its class is ignored" do
742
+ Bugsnag.configuration.ignore_classes << BugsnagTestException
716
743
 
717
- expect(Bugsnag).not_to have_sent_notification
718
- end
744
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
719
745
 
720
- it "does not notify if the non-default exception class is added to the ignore_classes" do
721
- Bugsnag.configuration.ignore_classes << BugsnagTestException
746
+ expect(Bugsnag).not_to have_sent_notification
747
+ end
722
748
 
723
- Bugsnag.notify(BugsnagTestException.new("It crashed"))
749
+ it "ignores exception when its ancestor is ignored" do
750
+ Bugsnag.configuration.ignore_classes << BugsnagTestException
724
751
 
725
- expect(Bugsnag).not_to have_sent_notification
726
- end
752
+ Bugsnag.notify(BugsnagSubclassTestException.new("It crashed"))
727
753
 
728
- it "does not notify if exception's ancestor is an ignored class" do
729
- Bugsnag.configuration.ignore_classes << BugsnagTestException
754
+ expect(Bugsnag).not_to have_sent_notification
755
+ end
730
756
 
731
- Bugsnag.notify(BugsnagSubclassTestException.new("It crashed"))
757
+ it "ignores exception when the original exception is ignored" do
758
+ Bugsnag.configuration.ignore_classes << BugsnagTestException
732
759
 
733
- expect(Bugsnag).not_to have_sent_notification
760
+ ex = NestedException.new("Self-referential exception")
761
+ ex.original_exception = BugsnagTestException.new("It crashed")
762
+
763
+ Bugsnag.notify(ex)
764
+
765
+ expect(Bugsnag).not_to have_sent_notification
766
+ end
767
+ end
768
+
769
+ context "as a proc" do
770
+ it "ignores exception when the proc returns true" do
771
+ Bugsnag.configuration.ignore_classes << ->(exception) { true }
772
+
773
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
774
+
775
+ expect(Bugsnag).not_to have_sent_notification
776
+ end
777
+
778
+ it "does not ignore exception when proc returns false" do
779
+ Bugsnag.configuration.ignore_classes << ->(exception) { false }
780
+
781
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
782
+
783
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
784
+ exception = get_exception_from_payload(payload)
785
+
786
+ expect(exception["errorClass"]).to eq("BugsnagTestException")
787
+ expect(exception["message"]).to eq("It crashed")
788
+ }
789
+ end
790
+ end
734
791
  end
735
792
 
736
- it "does not notify if any caused exception is an ignored class" do
737
- Bugsnag.configuration.ignore_classes << NestedException
793
+ context "discard_classes" do
794
+ context "as a string" do
795
+ it "discards exception when its class should be discarded" do
796
+ Bugsnag.configuration.discard_classes << "BugsnagTestException"
738
797
 
739
- ex = NestedException.new("Self-referential exception")
740
- ex.original_exception = BugsnagTestException.new("It crashed")
798
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
741
799
 
742
- Bugsnag.notify(ex)
800
+ expect(Bugsnag).not_to have_sent_notification
801
+ end
743
802
 
744
- expect(Bugsnag).not_to have_sent_notification
803
+ it "discards exception when the original exception should be discarded" do
804
+ Bugsnag.configuration.discard_classes << "BugsnagTestException"
805
+
806
+ ex = NestedException.new("Self-referential exception")
807
+ ex.original_exception = BugsnagTestException.new("It crashed")
808
+
809
+ Bugsnag.notify(ex)
810
+
811
+ expect(Bugsnag).not_to have_sent_notification
812
+ end
813
+
814
+ it "does not discard exception with a typo" do
815
+ Bugsnag.configuration.discard_classes << "BugsnagToastException"
816
+
817
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
818
+
819
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
820
+ exception = get_exception_from_payload(payload)
821
+
822
+ expect(exception["errorClass"]).to eq("BugsnagTestException")
823
+ expect(exception["message"]).to eq("It crashed")
824
+ }
825
+ end
826
+
827
+ it "does not discard exception when its ancestor is discarded" do
828
+ Bugsnag.configuration.discard_classes << "BugsnagTestException"
829
+
830
+ Bugsnag.notify(BugsnagSubclassTestException.new("It crashed"))
831
+
832
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
833
+ exception = get_exception_from_payload(payload)
834
+
835
+ expect(exception["errorClass"]).to eq("BugsnagSubclassTestException")
836
+ expect(exception["message"]).to eq("It crashed")
837
+ }
838
+ end
839
+ end
840
+
841
+ context "as a regexp" do
842
+ it "discards exception when its class should be discarded" do
843
+ Bugsnag.configuration.discard_classes << /^BugsnagTest.*/
844
+
845
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
846
+
847
+ expect(Bugsnag).not_to have_sent_notification
848
+ end
849
+
850
+ it "discards exception when the original exception should be discarded" do
851
+ Bugsnag.configuration.discard_classes << /^BugsnagTest.*/
852
+
853
+ ex = NestedException.new("Self-referential exception")
854
+ ex.original_exception = BugsnagTestException.new("It crashed")
855
+
856
+ Bugsnag.notify(ex)
857
+
858
+ expect(Bugsnag).not_to have_sent_notification
859
+ end
860
+
861
+ it "does not discard exception when regexp does not match" do
862
+ Bugsnag.configuration.discard_classes << /^NotBugsnag.*/
863
+
864
+ Bugsnag.notify(BugsnagTestException.new("It crashed"))
865
+
866
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
867
+ exception = get_exception_from_payload(payload)
868
+
869
+ expect(exception["errorClass"]).to eq("BugsnagTestException")
870
+ expect(exception["message"]).to eq("It crashed")
871
+ }
872
+ end
873
+
874
+ it "does not discard exception when its ancestor is discarded" do
875
+ Bugsnag.configuration.discard_classes << /^BugsnagTest.*/
876
+
877
+ Bugsnag.notify(BugsnagSubclassTestException.new("It crashed"))
878
+
879
+ expect(Bugsnag).to have_sent_notification { |payload, headers|
880
+ exception = get_exception_from_payload(payload)
881
+
882
+ expect(exception["errorClass"]).to eq("BugsnagSubclassTestException")
883
+ expect(exception["message"]).to eq("It crashed")
884
+ }
885
+ end
886
+ end
745
887
  end
746
888
 
747
889
  it "sends the cause of the exception" do
@@ -989,6 +1131,161 @@ describe Bugsnag::Report do
989
1131
  }
990
1132
  end
991
1133
 
1134
+ it "should handle recursive metadata" do
1135
+ a = [1, 2, 3]
1136
+ b = [2, a]
1137
+ a << b
1138
+ c = [1, 2, 3]
1139
+
1140
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
1141
+ report.add_tab(:some_tab, {
1142
+ a: a,
1143
+ b: b,
1144
+ c: c
1145
+ })
1146
+ end
1147
+
1148
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
1149
+ event = get_event_from_payload(payload)
1150
+ expect(event["metaData"]["some_tab"]).to eq({
1151
+ "a" => [1, 2, 3, [2, "[RECURSION]"]],
1152
+ "b" => [2, "[RECURSION]"],
1153
+ "c" => [1, 2, 3]
1154
+ })
1155
+ }
1156
+ end
1157
+
1158
+ it "does not detect two equal objects as recursion" do
1159
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
1160
+ report.add_tab(:some_tab, {
1161
+ data: [1, [1, 2], [1, 2], "a"]
1162
+ })
1163
+ end
1164
+
1165
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
1166
+ event = get_event_from_payload(payload)
1167
+ expect(event["metaData"]["some_tab"]).to eq({
1168
+ "data" => [1, [1, 2], [1, 2], "a"]
1169
+ })
1170
+ }
1171
+ end
1172
+
1173
+ context "an object that throws if `to_s` is called" do
1174
+ class StringRaiser
1175
+ def to_s
1176
+ raise 'Oh no you do not!'
1177
+ end
1178
+ end
1179
+
1180
+ it "uses the string '[RAISED]' instead" do
1181
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
1182
+ report.add_tab(:some_tab, {
1183
+ data: [1, 2, StringRaiser.new]
1184
+ })
1185
+ end
1186
+
1187
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
1188
+ event = get_event_from_payload(payload)
1189
+ expect(event["metaData"]["some_tab"]).to eq({
1190
+ "data" => [1, 2, "[RAISED]"]
1191
+ })
1192
+ }
1193
+ end
1194
+
1195
+ it "replaces hash key with '[RAISED]'" do
1196
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
1197
+ report.add_tab(:some_tab, {
1198
+ StringRaiser.new => 1
1199
+ })
1200
+ end
1201
+
1202
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
1203
+ event = get_event_from_payload(payload)
1204
+ expect(event["metaData"]["some_tab"]).to eq({
1205
+ "[RAISED]" => "[FILTERED]"
1206
+ })
1207
+ }
1208
+ end
1209
+
1210
+ it "uses a single '[RAISED]'key when multiple keys raise" do
1211
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
1212
+ report.add_tab(:some_tab, {
1213
+ StringRaiser.new => 1,
1214
+ StringRaiser.new => 2
1215
+ })
1216
+ end
1217
+
1218
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
1219
+ event = get_event_from_payload(payload)
1220
+ expect(event["metaData"]["some_tab"]).to eq({
1221
+ "[RAISED]" => "[FILTERED]"
1222
+ })
1223
+ }
1224
+ end
1225
+ end
1226
+
1227
+ context "an object that infinitely recurse if `to_s` is called" do
1228
+ is_jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
1229
+
1230
+ class StringRecurser
1231
+ def to_s
1232
+ to_s
1233
+ end
1234
+ end
1235
+
1236
+ it "uses the string '[RECURSION]' instead" do
1237
+ skip "JRuby doesn't allow recovery from SystemStackErrors" if is_jruby
1238
+
1239
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
1240
+ report.add_tab(:some_tab, {
1241
+ data: [1, 2, StringRecurser.new]
1242
+ })
1243
+ end
1244
+
1245
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
1246
+ event = get_event_from_payload(payload)
1247
+ expect(event["metaData"]["some_tab"]).to eq({
1248
+ "data" => [1, 2, "[RECURSION]"]
1249
+ })
1250
+ }
1251
+ end
1252
+
1253
+ it "replaces hash key with '[RECURSION]'" do
1254
+ skip "JRuby doesn't allow recovery from SystemStackErrors" if is_jruby
1255
+
1256
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
1257
+ report.add_tab(:some_tab, {
1258
+ StringRecurser.new => 1
1259
+ })
1260
+ end
1261
+
1262
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
1263
+ event = get_event_from_payload(payload)
1264
+ expect(event["metaData"]["some_tab"]).to eq({
1265
+ "[RECURSION]" => "[FILTERED]"
1266
+ })
1267
+ }
1268
+ end
1269
+
1270
+ it "uses a single '[RECURSION]'key when multiple keys recurse" do
1271
+ skip "JRuby doesn't allow recovery from SystemStackErrors" if is_jruby
1272
+
1273
+ Bugsnag.notify(BugsnagTestException.new("It crashed")) do |report|
1274
+ report.add_tab(:some_tab, {
1275
+ StringRecurser.new => 1,
1276
+ StringRecurser.new => 2
1277
+ })
1278
+ end
1279
+
1280
+ expect(Bugsnag).to have_sent_notification{ |payload, headers|
1281
+ event = get_event_from_payload(payload)
1282
+ expect(event["metaData"]["some_tab"]).to eq({
1283
+ "[RECURSION]" => "[FILTERED]"
1284
+ })
1285
+ }
1286
+ end
1287
+ end
1288
+
992
1289
  it 'should handle exceptions with empty backtrace' do
993
1290
  begin
994
1291
  err = RuntimeError.new
@@ -1280,3 +1577,4 @@ describe Bugsnag::Report do
1280
1577
  }
1281
1578
  end
1282
1579
  end
1580
+ # rubocop:enable Metrics/BlockLength