airbrake-ruby 4.1.0 → 5.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.
- checksums.yaml +5 -5
- data/lib/airbrake-ruby/async_sender.rb +22 -96
- data/lib/airbrake-ruby/backtrace.rb +8 -7
- data/lib/airbrake-ruby/benchmark.rb +39 -0
- data/lib/airbrake-ruby/code_hunk.rb +1 -1
- data/lib/airbrake-ruby/config/processor.rb +84 -0
- data/lib/airbrake-ruby/config/validator.rb +9 -3
- data/lib/airbrake-ruby/config.rb +76 -20
- data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
- data/lib/airbrake-ruby/file_cache.rb +6 -0
- data/lib/airbrake-ruby/filter_chain.rb +16 -1
- data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +5 -5
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +3 -0
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +2 -0
- data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
- data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
- data/lib/airbrake-ruby/filters/keys_filter.rb +39 -20
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +30 -6
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +4 -2
- data/lib/airbrake-ruby/grouppable.rb +12 -0
- data/lib/airbrake-ruby/ignorable.rb +1 -0
- data/lib/airbrake-ruby/inspectable.rb +2 -2
- data/lib/airbrake-ruby/loggable.rb +2 -2
- data/lib/airbrake-ruby/mergeable.rb +12 -0
- data/lib/airbrake-ruby/monotonic_time.rb +48 -0
- data/lib/airbrake-ruby/notice.rb +10 -20
- data/lib/airbrake-ruby/notice_notifier.rb +23 -42
- data/lib/airbrake-ruby/performance_breakdown.rb +52 -0
- data/lib/airbrake-ruby/performance_notifier.rb +126 -49
- data/lib/airbrake-ruby/promise.rb +1 -0
- data/lib/airbrake-ruby/query.rb +26 -11
- data/lib/airbrake-ruby/queue.rb +65 -0
- data/lib/airbrake-ruby/remote_settings/settings_data.rb +120 -0
- data/lib/airbrake-ruby/remote_settings.rb +145 -0
- data/lib/airbrake-ruby/request.rb +20 -6
- data/lib/airbrake-ruby/stashable.rb +15 -0
- data/lib/airbrake-ruby/stat.rb +34 -24
- data/lib/airbrake-ruby/sync_sender.rb +3 -2
- data/lib/airbrake-ruby/tdigest.rb +43 -58
- data/lib/airbrake-ruby/thread_pool.rb +138 -0
- data/lib/airbrake-ruby/timed_trace.rb +58 -0
- data/lib/airbrake-ruby/truncator.rb +10 -4
- data/lib/airbrake-ruby/version.rb +11 -1
- data/lib/airbrake-ruby.rb +219 -53
- data/spec/airbrake_spec.rb +428 -9
- data/spec/async_sender_spec.rb +26 -110
- data/spec/backtrace_spec.rb +44 -44
- data/spec/benchmark_spec.rb +33 -0
- data/spec/code_hunk_spec.rb +11 -11
- data/spec/config/processor_spec.rb +209 -0
- data/spec/config/validator_spec.rb +23 -6
- data/spec/config_spec.rb +77 -7
- data/spec/deploy_notifier_spec.rb +2 -2
- data/spec/{file_cache.rb → file_cache_spec.rb} +2 -4
- data/spec/filter_chain_spec.rb +28 -1
- data/spec/filters/dependency_filter_spec.rb +1 -1
- data/spec/filters/gem_root_filter_spec.rb +9 -9
- data/spec/filters/git_last_checkout_filter_spec.rb +21 -4
- data/spec/filters/git_repository_filter.rb +1 -1
- data/spec/filters/git_revision_filter_spec.rb +13 -11
- data/spec/filters/{keys_whitelist_spec.rb → keys_allowlist_spec.rb} +29 -28
- data/spec/filters/{keys_blacklist_spec.rb → keys_blocklist_spec.rb} +39 -29
- data/spec/filters/root_directory_filter_spec.rb +9 -9
- data/spec/filters/sql_filter_spec.rb +110 -55
- data/spec/filters/system_exit_filter_spec.rb +1 -1
- data/spec/filters/thread_filter_spec.rb +33 -31
- data/spec/fixtures/project_root/code.rb +9 -9
- data/spec/loggable_spec.rb +17 -0
- data/spec/monotonic_time_spec.rb +23 -0
- data/spec/{notice_notifier_spec → notice_notifier}/options_spec.rb +19 -21
- data/spec/notice_notifier_spec.rb +20 -80
- data/spec/notice_spec.rb +9 -11
- data/spec/performance_breakdown_spec.rb +11 -0
- data/spec/performance_notifier_spec.rb +360 -85
- data/spec/query_spec.rb +11 -0
- data/spec/queue_spec.rb +18 -0
- data/spec/remote_settings/settings_data_spec.rb +365 -0
- data/spec/remote_settings_spec.rb +230 -0
- data/spec/request_spec.rb +9 -0
- data/spec/response_spec.rb +8 -8
- data/spec/spec_helper.rb +9 -13
- data/spec/stashable_spec.rb +23 -0
- data/spec/stat_spec.rb +17 -15
- data/spec/sync_sender_spec.rb +14 -12
- data/spec/tdigest_spec.rb +6 -6
- data/spec/thread_pool_spec.rb +187 -0
- data/spec/timed_trace_spec.rb +125 -0
- data/spec/truncator_spec.rb +12 -12
- metadata +55 -18
data/spec/airbrake_spec.rb
CHANGED
@@ -1,7 +1,33 @@
|
|
1
1
|
RSpec.describe Airbrake do
|
2
|
-
|
2
|
+
let(:remote_settings) { instance_double(Airbrake::RemoteSettings) }
|
3
|
+
|
4
|
+
before do
|
5
|
+
allow(Airbrake::RemoteSettings).to receive(:poll).and_return(remote_settings)
|
6
|
+
allow(remote_settings).to receive(:stop_polling)
|
7
|
+
end
|
8
|
+
|
9
|
+
after { described_class.instance_variable_set(:@remote_settings, nil) }
|
10
|
+
|
11
|
+
it "gets initialized with a performance notifier" do
|
12
|
+
expect(described_class.performance_notifier).not_to be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "gets initialized with a notice notifier" do
|
16
|
+
expect(described_class.notice_notifier).not_to be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it "gets initialized with a deploy notifier" do
|
20
|
+
expect(described_class.deploy_notifier).not_to be_nil
|
21
|
+
end
|
3
22
|
|
4
23
|
describe ".configure" do
|
24
|
+
before do
|
25
|
+
Airbrake::Config.instance = Airbrake::Config.new
|
26
|
+
described_class.reset
|
27
|
+
end
|
28
|
+
|
29
|
+
after { described_class.reset }
|
30
|
+
|
5
31
|
it "yields the config" do
|
6
32
|
expect do |b|
|
7
33
|
begin
|
@@ -23,17 +49,410 @@ RSpec.describe Airbrake do
|
|
23
49
|
expect(Airbrake::Loggable.instance).to eql(logger)
|
24
50
|
end
|
25
51
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
52
|
+
it "makes Airbrake configured" do
|
53
|
+
expect(described_class).not_to be_configured
|
54
|
+
|
55
|
+
described_class.configure do |c|
|
56
|
+
c.project_id = 1
|
57
|
+
c.project_key = '2'
|
58
|
+
end
|
59
|
+
|
60
|
+
expect(described_class).to be_configured
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when called multiple times" do
|
64
|
+
it "doesn't overwrite performance notifier" do
|
65
|
+
described_class.configure {}
|
66
|
+
performance_notifier = described_class.performance_notifier
|
67
|
+
|
68
|
+
described_class.configure {}
|
69
|
+
expect(described_class.performance_notifier).to eql(performance_notifier)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "doesn't overwrite notice notifier" do
|
73
|
+
described_class.configure {}
|
74
|
+
notice_notifier = described_class.notice_notifier
|
75
|
+
|
76
|
+
described_class.configure {}
|
77
|
+
expect(described_class.notice_notifier).to eql(notice_notifier)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "doesn't overwrite deploy notifier" do
|
81
|
+
described_class.configure {}
|
82
|
+
deploy_notifier = described_class.deploy_notifier
|
83
|
+
|
84
|
+
described_class.configure {}
|
85
|
+
expect(described_class.deploy_notifier).to eql(deploy_notifier)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "doesn't append the same notice notifier filters over and over" do
|
89
|
+
described_class.configure do |c|
|
90
|
+
c.project_id = 1
|
91
|
+
c.project_key = '2'
|
92
|
+
end
|
93
|
+
|
94
|
+
expect(described_class.notice_notifier).not_to receive(:add_filter)
|
95
|
+
10.times { described_class.configure {} }
|
96
|
+
end
|
97
|
+
|
98
|
+
it "appends some default filters" do
|
99
|
+
allow(described_class.notice_notifier).to receive(:add_filter)
|
100
|
+
expect(described_class.notice_notifier).to receive(:add_filter).with(
|
101
|
+
an_instance_of(Airbrake::Filters::RootDirectoryFilter),
|
102
|
+
)
|
103
|
+
|
104
|
+
described_class.configure do |c|
|
105
|
+
c.project_id = 1
|
106
|
+
c.project_key = '2'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when blocklist_keys gets configured" do
|
112
|
+
before { allow(described_class.notice_notifier).to receive(:add_filter) }
|
113
|
+
|
114
|
+
it "adds blocklist filter" do
|
115
|
+
expect(described_class.notice_notifier).to receive(:add_filter)
|
116
|
+
.with(an_instance_of(Airbrake::Filters::KeysBlocklist))
|
117
|
+
described_class.configure { |c| c.blocklist_keys = %w[password] }
|
118
|
+
end
|
119
|
+
|
120
|
+
it "initializes blocklist with specified parameters" do
|
121
|
+
expect(Airbrake::Filters::KeysBlocklist).to receive(:new).with(%w[password])
|
122
|
+
described_class.configure { |c| c.blocklist_keys = %w[password] }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when allowlist_keys gets configured" do
|
127
|
+
before { allow(described_class.notice_notifier).to receive(:add_filter) }
|
128
|
+
|
129
|
+
it "adds allowlist filter" do
|
130
|
+
expect(described_class.notice_notifier).to receive(:add_filter)
|
131
|
+
.with(an_instance_of(Airbrake::Filters::KeysAllowlist))
|
132
|
+
described_class.configure { |c| c.allowlist_keys = %w[banana] }
|
133
|
+
end
|
134
|
+
|
135
|
+
it "initializes allowlist with specified parameters" do
|
136
|
+
expect(Airbrake::Filters::KeysAllowlist).to receive(:new).with(%w[banana])
|
137
|
+
described_class.configure { |c| c.allowlist_keys = %w[banana] }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when root_directory gets configured" do
|
142
|
+
before { allow(described_class.notice_notifier).to receive(:add_filter) }
|
143
|
+
|
144
|
+
it "adds root directory filter" do
|
145
|
+
expect(described_class.notice_notifier).to receive(:add_filter)
|
146
|
+
.with(an_instance_of(Airbrake::Filters::RootDirectoryFilter))
|
147
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
148
|
+
end
|
149
|
+
|
150
|
+
it "initializes root directory filter with specified path" do
|
151
|
+
expect(Airbrake::Filters::RootDirectoryFilter)
|
152
|
+
.to receive(:new).with('/my/path')
|
153
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
154
|
+
end
|
155
|
+
|
156
|
+
it "adds git revision filter" do
|
157
|
+
expect(described_class.notice_notifier).to receive(:add_filter)
|
158
|
+
.with(an_instance_of(Airbrake::Filters::GitRevisionFilter))
|
159
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
160
|
+
end
|
161
|
+
|
162
|
+
it "initializes git revision filter with correct root directory" do
|
163
|
+
expect(Airbrake::Filters::GitRevisionFilter)
|
164
|
+
.to receive(:new).with('/my/path')
|
165
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
166
|
+
end
|
167
|
+
|
168
|
+
it "adds git repository filter" do
|
169
|
+
expect(described_class.notice_notifier).to receive(:add_filter)
|
170
|
+
.with(an_instance_of(Airbrake::Filters::GitRepositoryFilter))
|
171
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
172
|
+
end
|
173
|
+
|
174
|
+
it "initializes git repository filter with correct root directory" do
|
175
|
+
expect(Airbrake::Filters::GitRepositoryFilter)
|
176
|
+
.to receive(:new).with('/my/path')
|
177
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
178
|
+
end
|
179
|
+
|
180
|
+
it "adds git last checkout filter" do
|
181
|
+
expect(described_class.notice_notifier).to receive(:add_filter)
|
182
|
+
.with(an_instance_of(Airbrake::Filters::GitLastCheckoutFilter))
|
183
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
184
|
+
end
|
185
|
+
|
186
|
+
it "initializes git last checkout filter with correct root directory" do
|
187
|
+
expect(Airbrake::Filters::GitLastCheckoutFilter)
|
188
|
+
.to receive(:new).with('/my/path')
|
189
|
+
described_class.configure { |c| c.root_directory = '/my/path' }
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe ".notify_request" do
|
195
|
+
context "when :stash key is not provided" do
|
196
|
+
it "doesn't add anything to the stash of the request" do
|
197
|
+
expect(described_class.performance_notifier).to receive(:notify) do |request|
|
198
|
+
expect(request.stash).to be_empty
|
199
|
+
end
|
200
|
+
|
201
|
+
described_class.notify_request(
|
202
|
+
method: 'GET',
|
203
|
+
route: '/',
|
204
|
+
status_code: 200,
|
205
|
+
timing: 1,
|
206
|
+
)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context "when :stash key is provided" do
|
211
|
+
it "adds the value as the stash of the request" do
|
212
|
+
expect(described_class.performance_notifier).to receive(:notify) do |request|
|
213
|
+
expect(request.stash).to eq(request_id: 1)
|
214
|
+
end
|
215
|
+
|
216
|
+
described_class.notify_request(
|
217
|
+
{
|
218
|
+
method: 'GET',
|
219
|
+
route: '/',
|
220
|
+
status_code: 200,
|
221
|
+
timing: 1,
|
222
|
+
},
|
223
|
+
request_id: 1,
|
224
|
+
)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe ".notify_request_sync" do
|
230
|
+
it "notifies request synchronously" do
|
231
|
+
expect(described_class.performance_notifier).to receive(:notify_sync)
|
232
|
+
|
233
|
+
described_class.notify_request_sync(
|
234
|
+
{
|
235
|
+
method: 'GET',
|
236
|
+
route: '/',
|
237
|
+
status_code: 200,
|
238
|
+
timing: 1,
|
239
|
+
},
|
240
|
+
request_id: 1,
|
241
|
+
)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe ".notify_query" do
|
246
|
+
context "when :stash key is not provided" do
|
247
|
+
it "doesn't add anything to the stash of the query" do
|
248
|
+
expect(described_class.performance_notifier).to receive(:notify) do |query|
|
249
|
+
expect(query.stash).to be_empty
|
250
|
+
end
|
251
|
+
|
252
|
+
described_class.notify_query(
|
253
|
+
method: 'GET',
|
254
|
+
route: '/',
|
255
|
+
query: '',
|
256
|
+
timing: 1,
|
257
|
+
)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
context "when :stash key is provided" do
|
262
|
+
it "adds the value as the stash of the query" do
|
263
|
+
expect(described_class.performance_notifier).to receive(:notify) do |query|
|
264
|
+
expect(query.stash).to eq(request_id: 1)
|
265
|
+
end
|
266
|
+
|
267
|
+
described_class.notify_query(
|
268
|
+
{
|
269
|
+
method: 'GET',
|
270
|
+
route: '/',
|
271
|
+
query: '',
|
272
|
+
timing: 1,
|
273
|
+
},
|
274
|
+
request_id: 1,
|
275
|
+
)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe ".notify_query_sync" do
|
281
|
+
it "notifies query synchronously" do
|
282
|
+
expect(described_class.performance_notifier).to receive(:notify_sync)
|
283
|
+
|
284
|
+
described_class.notify_query_sync(
|
285
|
+
{
|
286
|
+
method: 'GET',
|
287
|
+
route: '/',
|
288
|
+
query: '',
|
289
|
+
timing: 1,
|
290
|
+
},
|
291
|
+
request_id: 1,
|
292
|
+
)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe ".notify_performance_breakdown" do
|
297
|
+
context "when :stash key is not provided" do
|
298
|
+
it "doesn't add anything to the stash of the performance breakdown" do
|
299
|
+
expect(described_class.performance_notifier).to receive(:notify) do |query|
|
300
|
+
expect(query.stash).to be_empty
|
301
|
+
end
|
302
|
+
|
303
|
+
described_class.notify_query(
|
304
|
+
method: 'GET',
|
305
|
+
route: '/',
|
306
|
+
query: '',
|
307
|
+
timing: 1,
|
308
|
+
)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
context "when :stash key is provided" do
|
313
|
+
it "adds the value as the stash of the performance breakdown" do
|
314
|
+
expect(
|
315
|
+
described_class.performance_notifier,
|
316
|
+
).to receive(:notify) do |performance_breakdown|
|
317
|
+
expect(performance_breakdown.stash).to eq(request_id: 1)
|
318
|
+
end
|
319
|
+
|
320
|
+
described_class.notify_performance_breakdown(
|
321
|
+
{
|
322
|
+
method: 'GET',
|
323
|
+
route: '/',
|
324
|
+
response_type: :html,
|
325
|
+
groups: {},
|
326
|
+
timing: 1,
|
327
|
+
},
|
328
|
+
request_id: 1,
|
329
|
+
)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
describe ".notify_performance_breakdown_sync" do
|
335
|
+
it "notifies performance breakdown synchronously" do
|
336
|
+
expect(described_class.performance_notifier).to receive(:notify_sync)
|
337
|
+
|
338
|
+
described_class.notify_performance_breakdown_sync(
|
339
|
+
{
|
340
|
+
method: 'GET',
|
341
|
+
route: '/',
|
342
|
+
response_type: :html,
|
343
|
+
groups: {},
|
344
|
+
timing: 1,
|
345
|
+
},
|
346
|
+
request_id: 1,
|
347
|
+
)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
describe ".notify_queue" do
|
352
|
+
context "when :stash key is not provided" do
|
353
|
+
it "doesn't add anything to the stash of the queue" do
|
354
|
+
expect(described_class.performance_notifier).to receive(:notify) do |queue|
|
355
|
+
expect(queue.stash).to be_empty
|
356
|
+
end
|
357
|
+
|
358
|
+
described_class.notify_queue(
|
359
|
+
queue: 'bananas',
|
360
|
+
error_count: 10,
|
361
|
+
)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
context "when :stash key is provided" do
|
366
|
+
it "adds the value as the stash of the queue" do
|
367
|
+
expect(described_class.performance_notifier).to receive(:notify) do |queue|
|
368
|
+
expect(queue.stash).to eq(request_id: 1)
|
369
|
+
end
|
370
|
+
|
371
|
+
described_class.notify_queue(
|
372
|
+
{
|
373
|
+
queue: 'bananas',
|
374
|
+
error_count: 10,
|
375
|
+
},
|
376
|
+
request_id: 1,
|
377
|
+
)
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
describe ".notify_queue_sync" do
|
383
|
+
it "notifies queue synchronously" do
|
384
|
+
expect(described_class.performance_notifier).to receive(:notify_sync)
|
385
|
+
|
386
|
+
described_class.notify_queue_sync(
|
387
|
+
{
|
388
|
+
queue: 'bananas',
|
389
|
+
error_count: 10,
|
390
|
+
},
|
391
|
+
request_id: 1,
|
392
|
+
)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
describe ".performance_notifier" do
|
397
|
+
it "returns a performance notifier" do
|
398
|
+
expect(described_class.performance_notifier)
|
399
|
+
.to be_an(Airbrake::PerformanceNotifier)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
describe ".notice_notifier" do
|
404
|
+
it "returns a notice notifier" do
|
405
|
+
expect(described_class.notice_notifier).to be_an(Airbrake::NoticeNotifier)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
describe ".deploy_notifier" do
|
410
|
+
it "returns a deploy notifier" do
|
411
|
+
expect(described_class.deploy_notifier).to be_an(Airbrake::DeployNotifier)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
describe ".close" do
|
416
|
+
after { described_class.reset }
|
417
|
+
|
418
|
+
context "when notice_notifier is defined" do
|
419
|
+
it "gets closed" do
|
420
|
+
expect(described_class.notice_notifier).to receive(:close)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
context "when notice_notifier is undefined" do
|
425
|
+
it "doesn't get closed (because it wasn't initialized)" do
|
426
|
+
described_class.instance_variable_set(:@notice_notifier, nil)
|
427
|
+
expect_any_instance_of(Airbrake::NoticeNotifier).not_to receive(:close)
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
context "when performance_notifier is defined" do
|
432
|
+
it "gets closed" do
|
433
|
+
expect(described_class.performance_notifier).to receive(:close)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
context "when perforance_notifier is undefined" do
|
438
|
+
it "doesn't get closed (because it wasn't initialized)" do
|
439
|
+
described_class.instance_variable_set(:@performance_notifier, nil)
|
440
|
+
expect_any_instance_of(Airbrake::PerformanceNotifier)
|
441
|
+
.not_to receive(:close)
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
context "when remote settings are defined" do
|
446
|
+
it "stops polling" do
|
447
|
+
described_class.instance_variable_set(:@remote_settings, remote_settings)
|
448
|
+
expect(remote_settings).to receive(:stop_polling)
|
30
449
|
end
|
31
450
|
end
|
32
451
|
|
33
|
-
context "when
|
34
|
-
it "
|
35
|
-
|
36
|
-
|
452
|
+
context "when remote settings are undefined" do
|
453
|
+
it "doesn't stop polling (because they weren't initialized)" do
|
454
|
+
described_class.instance_variable_set(:@remote_settings, nil)
|
455
|
+
expect(remote_settings).not_to receive(:stop_polling)
|
37
456
|
end
|
38
457
|
end
|
39
458
|
end
|
data/spec/async_sender_spec.rb
CHANGED
@@ -8,140 +8,56 @@ RSpec.describe Airbrake::AsyncSender do
|
|
8
8
|
Airbrake::Config.instance = Airbrake::Config.new(
|
9
9
|
project_id: '1',
|
10
10
|
workers: 3,
|
11
|
-
queue_size:
|
11
|
+
queue_size: 10,
|
12
12
|
)
|
13
|
-
|
14
|
-
allow(Airbrake::Loggable.instance).to receive(:debug)
|
15
|
-
expect(subject).to have_workers
|
16
13
|
end
|
17
14
|
|
18
15
|
describe "#send" do
|
19
|
-
|
20
|
-
|
21
|
-
subject.send(notice, Airbrake::Promise.new)
|
22
|
-
end
|
23
|
-
subject.close
|
24
|
-
|
25
|
-
expect(a_request(:post, endpoint)).to have_been_made.twice
|
26
|
-
end
|
27
|
-
|
28
|
-
context "when the queue is full" do
|
29
|
-
before do
|
30
|
-
allow(subject.unsent).to receive(:size).and_return(queue_size)
|
31
|
-
end
|
32
|
-
|
33
|
-
it "discards payload" do
|
34
|
-
200.times do
|
35
|
-
subject.send(notice, Airbrake::Promise.new)
|
36
|
-
end
|
16
|
+
context "when sender has the capacity to send" do
|
17
|
+
it "sends notices to Airbrake" do
|
18
|
+
2.times { subject.send(notice, Airbrake::Promise.new) }
|
37
19
|
subject.close
|
38
20
|
|
39
|
-
expect(a_request(:post, endpoint)).
|
21
|
+
expect(a_request(:post, endpoint)).to have_been_made.twice
|
40
22
|
end
|
41
23
|
|
42
|
-
it "
|
43
|
-
|
44
|
-
|
45
|
-
).exactly(15).times
|
46
|
-
|
47
|
-
15.times do
|
48
|
-
subject.send(notice, Airbrake::Promise.new)
|
49
|
-
end
|
24
|
+
it "returns a resolved promise" do
|
25
|
+
promise = Airbrake::Promise.new
|
26
|
+
subject.send(notice, promise)
|
50
27
|
subject.close
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
describe "#close" do
|
56
|
-
context "when there are no unsent notices" do
|
57
|
-
it "joins the spawned thread" do
|
58
|
-
workers = subject.workers.list
|
59
|
-
expect(workers).to all(be_alive)
|
60
28
|
|
61
|
-
|
62
|
-
expect(workers).to all(be_stop)
|
29
|
+
expect(promise).to be_resolved
|
63
30
|
end
|
64
31
|
end
|
65
32
|
|
66
|
-
context "when
|
67
|
-
|
68
|
-
|
69
|
-
|
33
|
+
context "when sender has exceeded the capacity to send" do
|
34
|
+
before do
|
35
|
+
Airbrake::Config.instance = Airbrake::Config.new(
|
36
|
+
project_id: '1',
|
37
|
+
workers: 0,
|
38
|
+
queue_size: 1,
|
70
39
|
)
|
71
|
-
expect(Airbrake::Loggable.instance).to receive(:debug).with(/closed/)
|
72
|
-
|
73
|
-
300.times { subject.send(notice, Airbrake::Promise.new) }
|
74
|
-
subject.close
|
75
40
|
end
|
76
41
|
|
77
|
-
it "
|
42
|
+
it "doesn't send the exceeded notices to Airbrake" do
|
43
|
+
15.times { subject.send(notice, Airbrake::Promise.new) }
|
78
44
|
subject.close
|
79
|
-
expect(subject.unsent.size).to be_zero
|
80
|
-
end
|
81
|
-
end
|
82
45
|
|
83
|
-
|
84
|
-
it "doesn't increase the unsent queue size" do
|
85
|
-
begin
|
86
|
-
subject.close
|
87
|
-
rescue Airbrake::Error
|
88
|
-
nil
|
89
|
-
end
|
90
|
-
|
91
|
-
expect(subject.unsent.size).to be_zero
|
46
|
+
expect(a_request(:post, endpoint)).not_to have_been_made
|
92
47
|
end
|
93
48
|
|
94
|
-
it "
|
49
|
+
it "returns a rejected promise" do
|
50
|
+
promise = nil
|
51
|
+
15.times do
|
52
|
+
promise = subject.send(notice, Airbrake::Promise.new)
|
53
|
+
end
|
95
54
|
subject.close
|
96
55
|
|
97
|
-
expect(
|
98
|
-
expect
|
99
|
-
|
56
|
+
expect(promise).to be_rejected
|
57
|
+
expect(promise.value).to eq(
|
58
|
+
'error' => "AsyncSender has reached its capacity of 1",
|
100
59
|
)
|
101
60
|
end
|
102
61
|
end
|
103
|
-
|
104
|
-
context "when workers were not spawned" do
|
105
|
-
it "correctly closes the notifier nevertheless" do
|
106
|
-
subject.close
|
107
|
-
expect(subject).to be_closed
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
describe "#has_workers?" do
|
113
|
-
it "returns false when the sender is not closed, but has 0 workers" do
|
114
|
-
subject.workers.list.each do |worker|
|
115
|
-
worker.kill.join
|
116
|
-
end
|
117
|
-
expect(subject).not_to have_workers
|
118
|
-
end
|
119
|
-
|
120
|
-
it "returns false when the sender is closed" do
|
121
|
-
subject.close
|
122
|
-
expect(subject).not_to have_workers
|
123
|
-
end
|
124
|
-
|
125
|
-
it "respawns workers on fork()", skip: %w[jruby rbx].include?(RUBY_ENGINE) do
|
126
|
-
pid = fork { expect(subject).to have_workers }
|
127
|
-
Process.wait(pid)
|
128
|
-
subject.close
|
129
|
-
expect(subject).not_to have_workers
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
describe "#spawn_workers" do
|
134
|
-
it "spawns alive threads in an enclosed ThreadGroup" do
|
135
|
-
expect(subject.workers).to be_a(ThreadGroup)
|
136
|
-
expect(subject.workers.list).to all(be_alive)
|
137
|
-
expect(subject.workers).to be_enclosed
|
138
|
-
|
139
|
-
subject.close
|
140
|
-
end
|
141
|
-
|
142
|
-
it "spawns exactly config.workers workers" do
|
143
|
-
expect(subject.workers.list.size).to eq(Airbrake::Config.instance.workers)
|
144
|
-
subject.close
|
145
|
-
end
|
146
62
|
end
|
147
63
|
end
|