bugsnag 6.13.1 → 6.14.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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