airbrake-ruby 4.15.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby/async_sender.rb +4 -2
  3. data/lib/airbrake-ruby/backtrace.rb +6 -5
  4. data/lib/airbrake-ruby/config/processor.rb +77 -0
  5. data/lib/airbrake-ruby/config/validator.rb +6 -0
  6. data/lib/airbrake-ruby/config.rb +44 -35
  7. data/lib/airbrake-ruby/context.rb +51 -0
  8. data/lib/airbrake-ruby/file_cache.rb +1 -1
  9. data/lib/airbrake-ruby/filter_chain.rb +3 -0
  10. data/lib/airbrake-ruby/filters/context_filter.rb +4 -5
  11. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  12. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +1 -1
  13. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  14. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +3 -4
  15. data/lib/airbrake-ruby/filters/git_repository_filter.rb +11 -2
  16. data/lib/airbrake-ruby/filters/git_revision_filter.rb +3 -1
  17. data/lib/airbrake-ruby/filters/keys_filter.rb +23 -15
  18. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  19. data/lib/airbrake-ruby/filters/sql_filter.rb +11 -11
  20. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  21. data/lib/airbrake-ruby/filters/thread_filter.rb +4 -3
  22. data/lib/airbrake-ruby/grouppable.rb +1 -1
  23. data/lib/airbrake-ruby/ignorable.rb +1 -2
  24. data/lib/airbrake-ruby/mergeable.rb +1 -1
  25. data/lib/airbrake-ruby/monotonic_time.rb +1 -1
  26. data/lib/airbrake-ruby/notice.rb +1 -8
  27. data/lib/airbrake-ruby/notice_notifier.rb +4 -4
  28. data/lib/airbrake-ruby/performance_breakdown.rb +1 -6
  29. data/lib/airbrake-ruby/performance_notifier.rb +40 -54
  30. data/lib/airbrake-ruby/promise.rb +1 -0
  31. data/lib/airbrake-ruby/query.rb +1 -6
  32. data/lib/airbrake-ruby/queue.rb +1 -8
  33. data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
  34. data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
  35. data/lib/airbrake-ruby/remote_settings.rb +128 -0
  36. data/lib/airbrake-ruby/request.rb +1 -8
  37. data/lib/airbrake-ruby/stat.rb +2 -13
  38. data/lib/airbrake-ruby/sync_sender.rb +3 -2
  39. data/lib/airbrake-ruby/tdigest.rb +12 -9
  40. data/lib/airbrake-ruby/thread_pool.rb +9 -6
  41. data/lib/airbrake-ruby/time_truncate.rb +2 -2
  42. data/lib/airbrake-ruby/timed_trace.rb +1 -3
  43. data/lib/airbrake-ruby/truncator.rb +8 -2
  44. data/lib/airbrake-ruby/version.rb +11 -1
  45. data/lib/airbrake-ruby.rb +44 -54
  46. data/spec/airbrake_spec.rb +178 -92
  47. data/spec/async_sender_spec.rb +10 -8
  48. data/spec/backtrace_spec.rb +39 -36
  49. data/spec/benchmark_spec.rb +7 -5
  50. data/spec/code_hunk_spec.rb +26 -17
  51. data/spec/config/processor_spec.rb +167 -0
  52. data/spec/config/validator_spec.rb +23 -3
  53. data/spec/config_spec.rb +43 -55
  54. data/spec/context_spec.rb +54 -0
  55. data/spec/deploy_notifier_spec.rb +6 -4
  56. data/spec/file_cache_spec.rb +1 -0
  57. data/spec/filter_chain_spec.rb +29 -24
  58. data/spec/filters/context_filter_spec.rb +14 -5
  59. data/spec/filters/dependency_filter_spec.rb +3 -1
  60. data/spec/filters/exception_attributes_filter_spec.rb +5 -3
  61. data/spec/filters/gem_root_filter_spec.rb +9 -6
  62. data/spec/filters/git_last_checkout_filter_spec.rb +10 -12
  63. data/spec/filters/{git_repository_filter.rb → git_repository_filter_spec.rb} +26 -15
  64. data/spec/filters/git_revision_filter_spec.rb +20 -20
  65. data/spec/filters/keys_allowlist_spec.rb +26 -16
  66. data/spec/filters/keys_blocklist_spec.rb +35 -18
  67. data/spec/filters/root_directory_filter_spec.rb +7 -7
  68. data/spec/filters/sql_filter_spec.rb +28 -28
  69. data/spec/filters/system_exit_filter_spec.rb +4 -2
  70. data/spec/filters/thread_filter_spec.rb +16 -14
  71. data/spec/loggable_spec.rb +2 -2
  72. data/spec/monotonic_time_spec.rb +8 -6
  73. data/spec/nested_exception_spec.rb +46 -46
  74. data/spec/notice_notifier/options_spec.rb +25 -15
  75. data/spec/notice_notifier_spec.rb +54 -49
  76. data/spec/notice_spec.rb +7 -3
  77. data/spec/performance_breakdown_spec.rb +0 -12
  78. data/spec/performance_notifier_spec.rb +69 -87
  79. data/spec/promise_spec.rb +38 -32
  80. data/spec/query_spec.rb +1 -11
  81. data/spec/queue_spec.rb +1 -13
  82. data/spec/remote_settings/callback_spec.rb +162 -0
  83. data/spec/remote_settings/settings_data_spec.rb +348 -0
  84. data/spec/remote_settings_spec.rb +201 -0
  85. data/spec/request_spec.rb +1 -13
  86. data/spec/response_spec.rb +34 -12
  87. data/spec/spec_helper.rb +4 -4
  88. data/spec/stashable_spec.rb +5 -5
  89. data/spec/stat_spec.rb +7 -14
  90. data/spec/sync_sender_spec.rb +52 -17
  91. data/spec/tdigest_spec.rb +61 -56
  92. data/spec/thread_pool_spec.rb +67 -58
  93. data/spec/time_truncate_spec.rb +23 -6
  94. data/spec/timed_trace_spec.rb +32 -30
  95. data/spec/truncator_spec.rb +72 -43
  96. metadata +67 -51
data/spec/promise_spec.rb CHANGED
@@ -1,15 +1,17 @@
1
1
  RSpec.describe Airbrake::Promise do
2
+ subject(:promise) { described_class.new }
3
+
2
4
  describe ".then" do
3
5
  let(:resolved_with) { [] }
4
6
  let(:rejected_with) { [] }
5
7
 
6
8
  context "when it is not resolved" do
7
9
  it "returns self" do
8
- expect(subject.then {}).to eq(subject)
10
+ expect(promise.then { anything }).to eq(promise)
9
11
  end
10
12
 
11
13
  it "doesn't call the resolve callbacks yet" do
12
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
14
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
13
15
  expect(resolved_with).to be_empty
14
16
  end
15
17
  end
@@ -17,12 +19,12 @@ RSpec.describe Airbrake::Promise do
17
19
  context "when it is resolved" do
18
20
  shared_examples "then specs" do
19
21
  it "returns self" do
20
- expect(subject.then {}).to eq(subject)
22
+ expect(promise.then { anything }).to eq(promise)
21
23
  end
22
24
 
23
25
  it "yields the resolved value" do
24
26
  yielded = nil
25
- subject.then { |value| yielded = value }
27
+ promise.then { |value| yielded = value }
26
28
  expect(yielded).to eq('id' => '123')
27
29
  end
28
30
 
@@ -37,29 +39,29 @@ RSpec.describe Airbrake::Promise do
37
39
 
38
40
  context "and there are some resolve and reject callbacks in place" do
39
41
  before do
40
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
41
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
42
- subject.resolve('id' => '123')
42
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
43
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
44
+ promise.resolve('id' => '123')
43
45
  end
44
46
 
45
47
  include_examples "then specs"
46
48
 
47
49
  it "registers the resolve callbacks" do
48
- subject.resolve('id' => '456')
50
+ promise.resolve('id' => '456')
49
51
  expect(resolved_with).to match_array([1, 2, 1, 2])
50
52
  end
51
53
  end
52
54
 
53
55
  context "and additional then callbacks are added" do
54
56
  before do
55
- subject.resolve('id' => '123')
56
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
57
+ promise.resolve('id' => '123')
58
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
57
59
  end
58
60
 
59
61
  include_examples "then specs"
60
62
 
61
63
  it "doesn't register new resolve callbacks" do
62
- subject.resolve('id' => '456')
64
+ promise.resolve('id' => '456')
63
65
  expect(resolved_with).to match_array([1, 2])
64
66
  end
65
67
  end
@@ -72,11 +74,11 @@ RSpec.describe Airbrake::Promise do
72
74
 
73
75
  context "when it is not rejected" do
74
76
  it "returns self" do
75
- expect(subject.then {}).to eq(subject)
77
+ expect(promise.then { anything }).to eq(promise)
76
78
  end
77
79
 
78
80
  it "doesn't call the reject callbacks yet" do
79
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
81
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
80
82
  expect(rejected_with).to be_empty
81
83
  end
82
84
  end
@@ -84,12 +86,12 @@ RSpec.describe Airbrake::Promise do
84
86
  context "when it is rejected" do
85
87
  shared_examples "rescue specs" do
86
88
  it "returns self" do
87
- expect(subject.rescue {}).to eq(subject)
89
+ expect(promise.rescue { anything }).to eq(promise)
88
90
  end
89
91
 
90
92
  it "yields the rejected value" do
91
93
  yielded = nil
92
- subject.rescue { |value| yielded = value }
94
+ promise.rescue { |value| yielded = value }
93
95
  expect(yielded).to eq('bingo')
94
96
  end
95
97
 
@@ -104,29 +106,29 @@ RSpec.describe Airbrake::Promise do
104
106
 
105
107
  context "and there are some resolve and reject callbacks in place" do
106
108
  before do
107
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
108
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
109
- subject.reject('bingo')
109
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
110
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
111
+ promise.reject('bingo')
110
112
  end
111
113
 
112
114
  include_examples "rescue specs"
113
115
 
114
116
  it "registers the reject callbacks" do
115
- subject.reject('bingo again')
117
+ promise.reject('bingo again')
116
118
  expect(rejected_with).to match_array([1, 2, 1, 2])
117
119
  end
118
120
  end
119
121
 
120
122
  context "and additional reject callbacks are added" do
121
123
  before do
122
- subject.reject('bingo')
123
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
124
+ promise.reject('bingo')
125
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
124
126
  end
125
127
 
126
128
  include_examples "rescue specs"
127
129
 
128
130
  it "doesn't register new reject callbacks" do
129
- subject.reject('bingo again')
131
+ promise.reject('bingo again')
130
132
  expect(rejected_with).to match_array([1, 2])
131
133
  end
132
134
  end
@@ -135,37 +137,38 @@ RSpec.describe Airbrake::Promise do
135
137
 
136
138
  describe ".resolve" do
137
139
  it "returns self" do
138
- expect(subject.resolve(1)).to eq(subject)
140
+ expect(promise.resolve(1)).to eq(promise)
139
141
  end
140
142
 
141
143
  it "executes callbacks attached with .then" do
142
144
  array = []
143
- subject.then { |notice_id| array << notice_id }.rescue { array << 999 }
145
+ promise.then { |notice_id| array << notice_id }.rescue { array << 999 }
144
146
 
145
147
  expect(array.size).to be_zero
146
- subject.resolve(1)
148
+ promise.resolve(1)
147
149
  expect(array).to match_array([1])
148
150
  end
149
151
  end
150
152
 
151
153
  describe ".reject" do
152
154
  it "returns self" do
153
- expect(subject.reject(1)).to eq(subject)
155
+ expect(promise.reject(1)).to eq(promise)
154
156
  end
155
157
 
156
158
  it "executes callbacks attached with .rescue" do
157
159
  array = []
158
- subject.then { array << 1 }.rescue { |error| array << error }
160
+ promise.then { array << 1 }.rescue { |error| array << error }
159
161
 
160
162
  expect(array.size).to be_zero
161
- subject.reject(999)
163
+ promise.reject(999)
162
164
  expect(array).to match_array([999])
163
165
  end
164
166
  end
165
167
 
166
168
  describe "#rejected?" do
167
169
  context "when it was rejected" do
168
- before { subject.reject(1) }
170
+ before { promise.reject(1) }
171
+
169
172
  it { is_expected.to be_rejected }
170
173
  end
171
174
 
@@ -174,14 +177,16 @@ RSpec.describe Airbrake::Promise do
174
177
  end
175
178
 
176
179
  context "when it was resolved" do
177
- before { subject.resolve }
180
+ before { promise.resolve }
181
+
178
182
  it { is_expected.not_to be_rejected }
179
183
  end
180
184
  end
181
185
 
182
186
  describe "#resolved?" do
183
187
  context "when it was resolved" do
184
- before { subject.resolve }
188
+ before { promise.resolve }
189
+
185
190
  it { is_expected.to be_resolved }
186
191
  end
187
192
 
@@ -190,7 +195,8 @@ RSpec.describe Airbrake::Promise do
190
195
  end
191
196
 
192
197
  context "when it was rejected" do
193
- before { subject.reject(1) }
198
+ before { promise.reject(1) }
199
+
194
200
  it { is_expected.not_to be_resolved }
195
201
  end
196
202
  end
data/spec/query_spec.rb CHANGED
@@ -2,20 +2,10 @@ RSpec.describe Airbrake::Query do
2
2
  describe "#stash" do
3
3
  subject do
4
4
  described_class.new(
5
- method: 'GET', route: '/', query: '', start_time: Time.now,
5
+ method: 'GET', route: '/', query: '',
6
6
  )
7
7
  end
8
8
 
9
9
  it { is_expected.to respond_to(:stash) }
10
10
  end
11
-
12
- describe "#end_time" do
13
- it "is always equal to start_time + 1 second by default" do
14
- time = Time.now
15
- query = described_class.new(
16
- method: 'GET', route: '/', query: '', start_time: time,
17
- )
18
- expect(query.end_time).to eq(time + 1)
19
- end
20
- end
21
11
  end
data/spec/queue_spec.rb CHANGED
@@ -9,21 +9,9 @@ RSpec.describe Airbrake::Queue do
9
9
  it { is_expected.to respond_to(:stash) }
10
10
  end
11
11
 
12
- describe "#end_time" do
13
- it "is always equal to start_time + 1 second by default" do
14
- time = Time.now
15
- queue = described_class.new(
16
- queue: 'bananas', error_count: 0, start_time: time,
17
- )
18
- expect(queue.end_time).to eq(time + 1)
19
- end
20
- end
21
-
22
12
  describe "#route" do
23
13
  it "always returns an empty route" do
24
- queue = described_class.new(
25
- queue: 'a', error_count: 0, start_time: Time.now,
26
- )
14
+ queue = described_class.new(queue: 'a', error_count: 0)
27
15
  expect(queue.route).to be_empty
28
16
  end
29
17
  end
@@ -0,0 +1,162 @@
1
+ RSpec.describe Airbrake::RemoteSettings::Callback do
2
+ describe "#call" do
3
+ let(:logger) { Logger.new(File::NULL) }
4
+
5
+ let(:config) do
6
+ Airbrake::Config.new(
7
+ project_id: 123,
8
+ logger: logger,
9
+ )
10
+ end
11
+
12
+ let(:data) do
13
+ instance_double(Airbrake::RemoteSettings::SettingsData)
14
+ end
15
+
16
+ before do
17
+ allow(data).to receive(:to_h)
18
+ allow(data).to receive(:error_host)
19
+ allow(data).to receive(:apm_host)
20
+ allow(data).to receive(:error_notifications?)
21
+ allow(data).to receive(:performance_stats?)
22
+ end
23
+
24
+ it "logs given data" do
25
+ allow(logger).to receive(:debug)
26
+
27
+ described_class.new(config).call(data)
28
+
29
+ expect(logger).to have_received(:debug) do |&block|
30
+ expect(block.call).to match(/applying remote settings/)
31
+ end
32
+ end
33
+
34
+ context "when the config disables error notifications" do
35
+ before do
36
+ config.error_notifications = false
37
+ allow(data).to receive(:error_notifications?).and_return(true)
38
+ end
39
+
40
+ # rubocop:disable RSpec/MultipleExpectations
41
+ it "keeps the option disabled forever" do
42
+ callback = described_class.new(config)
43
+
44
+ callback.call(data)
45
+ expect(config.error_notifications).to be(false)
46
+
47
+ callback.call(data)
48
+ expect(config.error_notifications).to be(false)
49
+
50
+ callback.call(data)
51
+ expect(config.error_notifications).to be(false)
52
+ end
53
+ # rubocop:enable RSpec/MultipleExpectations
54
+ end
55
+
56
+ context "when the config enables error notifications" do
57
+ before { config.error_notifications = true }
58
+
59
+ # rubocop:disable RSpec/MultipleExpectations
60
+ it "can disable and enable error notifications" do
61
+ callback = described_class.new(config)
62
+
63
+ allow(data).to receive(:error_notifications?).and_return(false)
64
+
65
+ callback.call(data)
66
+ expect(config.error_notifications).to be(false)
67
+
68
+ allow(data).to receive(:error_notifications?).and_return(true)
69
+
70
+ callback.call(data)
71
+ expect(config.error_notifications).to be(true)
72
+
73
+ expect(data).to have_received(:error_notifications?).twice
74
+ end
75
+ # rubocop:enable RSpec/MultipleExpectations
76
+ end
77
+
78
+ context "when the config disables performance_stats" do
79
+ before do
80
+ config.performance_stats = false
81
+ allow(data).to receive(:performance_stats?).and_return(true)
82
+ end
83
+
84
+ # rubocop:disable RSpec/MultipleExpectations
85
+ it "keeps the option disabled forever" do
86
+ callback = described_class.new(config)
87
+
88
+ callback.call(data)
89
+ expect(config.performance_stats).to be(false)
90
+
91
+ callback.call(data)
92
+ expect(config.performance_stats).to be(false)
93
+
94
+ callback.call(data)
95
+ expect(config.performance_stats).to be(false)
96
+ end
97
+ # rubocop:enable RSpec/MultipleExpectations
98
+ end
99
+
100
+ context "when the config enables performance stats" do
101
+ before { config.performance_stats = true }
102
+
103
+ # rubocop:disable RSpec/MultipleExpectations
104
+ it "can disable and enable performance_stats" do
105
+ callback = described_class.new(config)
106
+
107
+ allow(data).to receive(:performance_stats?).and_return(false)
108
+
109
+ callback.call(data)
110
+ expect(config.performance_stats).to be(false)
111
+
112
+ allow(data).to receive(:performance_stats?).and_return(true)
113
+
114
+ callback.call(data)
115
+ expect(config.performance_stats).to be(true)
116
+
117
+ expect(data).to have_received(:performance_stats?).twice
118
+ end
119
+ # rubocop:enable RSpec/MultipleExpectations
120
+ end
121
+
122
+ context "when error_host returns a value" do
123
+ it "sets the error_host option" do
124
+ config.error_host = 'http://api.airbrake.io'
125
+ allow(data).to receive(:error_host).and_return('https://api.example.com')
126
+
127
+ described_class.new(config).call(data)
128
+ expect(config.error_host).to eq('https://api.example.com')
129
+ end
130
+ end
131
+
132
+ context "when error_host returns nil" do
133
+ it "doesn't modify the error_host option" do
134
+ config.error_host = 'http://api.airbrake.io'
135
+ allow(data).to receive(:error_host).and_return(nil)
136
+
137
+ described_class.new(config).call(data)
138
+ expect(config.error_host).to eq('http://api.airbrake.io')
139
+ end
140
+ end
141
+
142
+ context "when apm_host returns a value" do
143
+ it "sets the apm_host option" do
144
+ config.apm_host = 'http://api.airbrake.io'
145
+ allow(data).to receive(:apm_host).and_return('https://api.example.com')
146
+
147
+ described_class.new(config).call(data)
148
+ expect(config.apm_host).to eq('https://api.example.com')
149
+ end
150
+ end
151
+
152
+ context "when apm_host returns nil" do
153
+ it "doesn't modify the apm_host option" do
154
+ config.apm_host = 'http://api.airbrake.io'
155
+ allow(data).to receive(:apm_host).and_return(nil)
156
+
157
+ described_class.new(config).call(data)
158
+ expect(config.apm_host).to eq('http://api.airbrake.io')
159
+ end
160
+ end
161
+ end
162
+ end