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.
- checksums.yaml +4 -4
- data/.rubocop.yml +55 -0
- data/.rubocop_todo.yml +530 -160
- data/CHANGELOG.md +25 -0
- data/Gemfile +3 -1
- data/TESTING.md +3 -3
- data/VERSION +1 -1
- data/dockerfiles/Dockerfile.ruby-maze-runner +1 -1
- data/features/fixtures/rails3/app/config/initializers/bugsnag.rb +2 -1
- data/features/fixtures/rails4/app/config/initializers/bugsnag.rb +1 -0
- data/features/fixtures/rails5/app/config/initializers/bugsnag.rb +1 -0
- data/features/fixtures/rails6/app/config/initializers/bugsnag.rb +1 -0
- data/features/rails_features/meta_data_filters.feature +4 -2
- data/lib/bugsnag.rb +74 -21
- data/lib/bugsnag/breadcrumbs/breadcrumbs.rb +0 -2
- data/lib/bugsnag/breadcrumbs/validator.rb +0 -6
- data/lib/bugsnag/cleaner.rb +109 -56
- data/lib/bugsnag/configuration.rb +20 -2
- data/lib/bugsnag/helpers.rb +2 -4
- data/lib/bugsnag/middleware/discard_error_class.rb +30 -0
- data/lib/bugsnag/middleware/ignore_error_class.rb +2 -0
- data/lib/bugsnag/middleware/rack_request.rb +2 -4
- data/lib/bugsnag/report.rb +3 -13
- data/lib/bugsnag/stacktrace.rb +5 -6
- data/spec/breadcrumbs/breadcrumb_spec.rb +1 -1
- data/spec/breadcrumbs/validator_spec.rb +1 -26
- data/spec/bugsnag_spec.rb +2 -2
- data/spec/cleaner_spec.rb +116 -10
- data/spec/configuration_spec.rb +5 -1
- data/spec/helper_spec.rb +0 -86
- data/spec/integrations/rack_spec.rb +8 -6
- data/spec/report_spec.rb +324 -26
- data/spec/spec_helper.rb +6 -1
- data/spec/stacktrace_spec.rb +141 -73
- metadata +3 -2
data/spec/configuration_spec.rb
CHANGED
@@ -332,10 +332,14 @@ describe Bugsnag::Configuration do
|
|
332
332
|
end
|
333
333
|
end
|
334
334
|
|
335
|
-
it "should have exit exception classes
|
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
|
data/spec/helper_spec.rb
CHANGED
@@ -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 =
|
131
|
-
|
132
|
-
|
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 =
|
191
|
-
|
192
|
-
|
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, {
|
data/spec/report_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
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
|
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
|
-
|
714
|
-
|
715
|
-
|
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
|
-
|
718
|
-
end
|
744
|
+
Bugsnag.notify(BugsnagTestException.new("It crashed"))
|
719
745
|
|
720
|
-
|
721
|
-
|
746
|
+
expect(Bugsnag).not_to have_sent_notification
|
747
|
+
end
|
722
748
|
|
723
|
-
|
749
|
+
it "ignores exception when its ancestor is ignored" do
|
750
|
+
Bugsnag.configuration.ignore_classes << BugsnagTestException
|
724
751
|
|
725
|
-
|
726
|
-
end
|
752
|
+
Bugsnag.notify(BugsnagSubclassTestException.new("It crashed"))
|
727
753
|
|
728
|
-
|
729
|
-
|
754
|
+
expect(Bugsnag).not_to have_sent_notification
|
755
|
+
end
|
730
756
|
|
731
|
-
|
757
|
+
it "ignores exception when the original exception is ignored" do
|
758
|
+
Bugsnag.configuration.ignore_classes << BugsnagTestException
|
732
759
|
|
733
|
-
|
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
|
-
|
737
|
-
|
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
|
-
|
740
|
-
ex.original_exception = BugsnagTestException.new("It crashed")
|
798
|
+
Bugsnag.notify(BugsnagTestException.new("It crashed"))
|
741
799
|
|
742
|
-
|
800
|
+
expect(Bugsnag).not_to have_sent_notification
|
801
|
+
end
|
743
802
|
|
744
|
-
|
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
|