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.
Files changed (94) hide show
  1. checksums.yaml +5 -5
  2. data/lib/airbrake-ruby/async_sender.rb +22 -96
  3. data/lib/airbrake-ruby/backtrace.rb +8 -7
  4. data/lib/airbrake-ruby/benchmark.rb +39 -0
  5. data/lib/airbrake-ruby/code_hunk.rb +1 -1
  6. data/lib/airbrake-ruby/config/processor.rb +84 -0
  7. data/lib/airbrake-ruby/config/validator.rb +9 -3
  8. data/lib/airbrake-ruby/config.rb +76 -20
  9. data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
  10. data/lib/airbrake-ruby/file_cache.rb +6 -0
  11. data/lib/airbrake-ruby/filter_chain.rb +16 -1
  12. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  13. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
  14. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  15. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +5 -5
  16. data/lib/airbrake-ruby/filters/git_repository_filter.rb +3 -0
  17. data/lib/airbrake-ruby/filters/git_revision_filter.rb +2 -0
  18. data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
  19. data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
  20. data/lib/airbrake-ruby/filters/keys_filter.rb +39 -20
  21. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  22. data/lib/airbrake-ruby/filters/sql_filter.rb +30 -6
  23. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  24. data/lib/airbrake-ruby/filters/thread_filter.rb +4 -2
  25. data/lib/airbrake-ruby/grouppable.rb +12 -0
  26. data/lib/airbrake-ruby/ignorable.rb +1 -0
  27. data/lib/airbrake-ruby/inspectable.rb +2 -2
  28. data/lib/airbrake-ruby/loggable.rb +2 -2
  29. data/lib/airbrake-ruby/mergeable.rb +12 -0
  30. data/lib/airbrake-ruby/monotonic_time.rb +48 -0
  31. data/lib/airbrake-ruby/notice.rb +10 -20
  32. data/lib/airbrake-ruby/notice_notifier.rb +23 -42
  33. data/lib/airbrake-ruby/performance_breakdown.rb +52 -0
  34. data/lib/airbrake-ruby/performance_notifier.rb +126 -49
  35. data/lib/airbrake-ruby/promise.rb +1 -0
  36. data/lib/airbrake-ruby/query.rb +26 -11
  37. data/lib/airbrake-ruby/queue.rb +65 -0
  38. data/lib/airbrake-ruby/remote_settings/settings_data.rb +120 -0
  39. data/lib/airbrake-ruby/remote_settings.rb +145 -0
  40. data/lib/airbrake-ruby/request.rb +20 -6
  41. data/lib/airbrake-ruby/stashable.rb +15 -0
  42. data/lib/airbrake-ruby/stat.rb +34 -24
  43. data/lib/airbrake-ruby/sync_sender.rb +3 -2
  44. data/lib/airbrake-ruby/tdigest.rb +43 -58
  45. data/lib/airbrake-ruby/thread_pool.rb +138 -0
  46. data/lib/airbrake-ruby/timed_trace.rb +58 -0
  47. data/lib/airbrake-ruby/truncator.rb +10 -4
  48. data/lib/airbrake-ruby/version.rb +11 -1
  49. data/lib/airbrake-ruby.rb +219 -53
  50. data/spec/airbrake_spec.rb +428 -9
  51. data/spec/async_sender_spec.rb +26 -110
  52. data/spec/backtrace_spec.rb +44 -44
  53. data/spec/benchmark_spec.rb +33 -0
  54. data/spec/code_hunk_spec.rb +11 -11
  55. data/spec/config/processor_spec.rb +209 -0
  56. data/spec/config/validator_spec.rb +23 -6
  57. data/spec/config_spec.rb +77 -7
  58. data/spec/deploy_notifier_spec.rb +2 -2
  59. data/spec/{file_cache.rb → file_cache_spec.rb} +2 -4
  60. data/spec/filter_chain_spec.rb +28 -1
  61. data/spec/filters/dependency_filter_spec.rb +1 -1
  62. data/spec/filters/gem_root_filter_spec.rb +9 -9
  63. data/spec/filters/git_last_checkout_filter_spec.rb +21 -4
  64. data/spec/filters/git_repository_filter.rb +1 -1
  65. data/spec/filters/git_revision_filter_spec.rb +13 -11
  66. data/spec/filters/{keys_whitelist_spec.rb → keys_allowlist_spec.rb} +29 -28
  67. data/spec/filters/{keys_blacklist_spec.rb → keys_blocklist_spec.rb} +39 -29
  68. data/spec/filters/root_directory_filter_spec.rb +9 -9
  69. data/spec/filters/sql_filter_spec.rb +110 -55
  70. data/spec/filters/system_exit_filter_spec.rb +1 -1
  71. data/spec/filters/thread_filter_spec.rb +33 -31
  72. data/spec/fixtures/project_root/code.rb +9 -9
  73. data/spec/loggable_spec.rb +17 -0
  74. data/spec/monotonic_time_spec.rb +23 -0
  75. data/spec/{notice_notifier_spec → notice_notifier}/options_spec.rb +19 -21
  76. data/spec/notice_notifier_spec.rb +20 -80
  77. data/spec/notice_spec.rb +9 -11
  78. data/spec/performance_breakdown_spec.rb +11 -0
  79. data/spec/performance_notifier_spec.rb +360 -85
  80. data/spec/query_spec.rb +11 -0
  81. data/spec/queue_spec.rb +18 -0
  82. data/spec/remote_settings/settings_data_spec.rb +365 -0
  83. data/spec/remote_settings_spec.rb +230 -0
  84. data/spec/request_spec.rb +9 -0
  85. data/spec/response_spec.rb +8 -8
  86. data/spec/spec_helper.rb +9 -13
  87. data/spec/stashable_spec.rb +23 -0
  88. data/spec/stat_spec.rb +17 -15
  89. data/spec/sync_sender_spec.rb +14 -12
  90. data/spec/tdigest_spec.rb +6 -6
  91. data/spec/thread_pool_spec.rb +187 -0
  92. data/spec/timed_trace_spec.rb +125 -0
  93. data/spec/truncator_spec.rb +12 -12
  94. metadata +55 -18
@@ -1,16 +1,22 @@
1
1
  RSpec.describe Airbrake::PerformanceNotifier do
2
2
  let(:routes) { 'https://api.airbrake.io/api/v5/projects/1/routes-stats' }
3
3
  let(:queries) { 'https://api.airbrake.io/api/v5/projects/1/queries-stats' }
4
+ let(:breakdowns) { 'https://api.airbrake.io/api/v5/projects/1/routes-breakdowns' }
5
+ let(:queues) { 'https://api.airbrake.io/api/v5/projects/1/queues-stats' }
4
6
 
5
7
  before do
6
8
  stub_request(:put, routes).to_return(status: 200, body: '')
7
9
  stub_request(:put, queries).to_return(status: 200, body: '')
10
+ stub_request(:put, breakdowns).to_return(status: 200, body: '')
11
+ stub_request(:put, queues).to_return(status: 200, body: '')
8
12
 
9
13
  Airbrake::Config.instance = Airbrake::Config.new(
10
14
  project_id: 1,
11
15
  project_key: 'banana',
12
16
  performance_stats: true,
13
- performance_stats_flush_period: 0
17
+ performance_stats_flush_period: 0,
18
+ query_stats: true,
19
+ job_stats: true,
14
20
  )
15
21
  end
16
22
 
@@ -24,10 +30,11 @@ RSpec.describe Airbrake::PerformanceNotifier do
24
30
  func: 'foo',
25
31
  file: 'foo.rb',
26
32
  line: 123,
27
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
28
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
29
- )
33
+ timing: 60000,
34
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
35
+ ),
30
36
  )
37
+ subject.close
31
38
 
32
39
  expect(
33
40
  a_request(:put, queries).with(body: %r|
@@ -43,7 +50,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
43
50
  "sum":60000.0,
44
51
  "sumsq":3600000000.0,
45
52
  "tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB"
46
- }\]}\z|x)
53
+ }\]}\z|x),
47
54
  ).to have_been_made
48
55
  end
49
56
 
@@ -53,10 +60,11 @@ RSpec.describe Airbrake::PerformanceNotifier do
53
60
  method: 'POST',
54
61
  route: '/foo',
55
62
  status_code: 200,
56
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
57
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
58
- )
63
+ timing: 60000,
64
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
65
+ ),
59
66
  )
67
+ subject.close
60
68
 
61
69
  expect(
62
70
  a_request(:put, routes).with(body: %r|
@@ -69,7 +77,89 @@ RSpec.describe Airbrake::PerformanceNotifier do
69
77
  "sum":60000.0,
70
78
  "sumsq":3600000000.0,
71
79
  "tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB"
72
- }\]}\z|x)
80
+ }\]}\z|x),
81
+ ).to have_been_made
82
+ end
83
+
84
+ it "sends full performance breakdown" do
85
+ subject.notify(
86
+ Airbrake::PerformanceBreakdown.new(
87
+ method: 'DELETE',
88
+ route: '/routes-breakdowns',
89
+ response_type: 'json',
90
+ timing: 60000,
91
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
92
+ groups: { db: 131, view: 421 },
93
+ ),
94
+ )
95
+ subject.close
96
+
97
+ expect(
98
+ a_request(:put, breakdowns).with(body: %r|
99
+ \A{"routes":\[{
100
+ "method":"DELETE",
101
+ "route":"/routes-breakdowns",
102
+ "responseType":"json",
103
+ "time":"2018-01-01T00:49:00\+00:00",
104
+ "count":1,
105
+ "sum":60000.0,
106
+ "sumsq":3600000000.0,
107
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB",
108
+ "groups":{
109
+ "db":{
110
+ "count":1,
111
+ "sum":131.0,
112
+ "sumsq":17161.0,
113
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUMDAAAB"
114
+ },
115
+ "view":{
116
+ "count":1,
117
+ "sum":421.0,
118
+ "sumsq":177241.0,
119
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUPSgAAB"
120
+ }
121
+ }
122
+ }\]}\z|x),
123
+ ).to have_been_made
124
+ end
125
+
126
+ it "sends full queue" do
127
+ subject.notify(
128
+ Airbrake::Queue.new(
129
+ queue: 'emails',
130
+ error_count: 2,
131
+ groups: { redis: 131, sql: 421 },
132
+ timing: 60000,
133
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
134
+ ),
135
+ )
136
+ subject.close
137
+
138
+ expect(
139
+ a_request(:put, queues).with(body: /
140
+ \A{"queues":\[{
141
+ "queue":"emails",
142
+ "errorCount":2,
143
+ "time":"2018-01-01T00:49:00\+00:00",
144
+ "count":1,
145
+ "sum":60000.0,
146
+ "sumsq":3600000000.0,
147
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB",
148
+ "groups":{
149
+ "redis":{
150
+ "count":1,
151
+ "sum":131.0,
152
+ "sumsq":17161.0,
153
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUMDAAAB"
154
+ },
155
+ "sql":{
156
+ "count":1,
157
+ "sum":421.0,
158
+ "sumsq":177241.0,
159
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUPSgAAB"
160
+ }
161
+ }
162
+ }\]}\z/x),
73
163
  ).to have_been_made
74
164
  end
75
165
 
@@ -79,11 +169,14 @@ RSpec.describe Airbrake::PerformanceNotifier do
79
169
  method: 'GET',
80
170
  route: '/foo',
81
171
  status_code: 200,
82
- start_time: Time.new(2018, 1, 1, 0, 0, 20, 0)
83
- )
172
+ timing: 60000,
173
+ time: Time.new(2018, 1, 1, 0, 0, 20, 0),
174
+ ),
84
175
  )
176
+ subject.close
177
+
85
178
  expect(
86
- a_request(:put, routes).with(body: /"time":"2018-01-01T00:00:00\+00:00"/)
179
+ a_request(:put, routes).with(body: /"time":"2018-01-01T00:00:00\+00:00"/),
87
180
  ).to have_been_made
88
181
  end
89
182
 
@@ -93,19 +186,21 @@ RSpec.describe Airbrake::PerformanceNotifier do
93
186
  method: 'GET',
94
187
  route: '/foo',
95
188
  status_code: 200,
96
- start_time: Time.new(2018, 1, 1, 0, 0, 20, 0)
97
- )
189
+ timing: 213,
190
+ ),
98
191
  )
99
192
  subject.notify(
100
193
  Airbrake::Request.new(
101
194
  method: 'GET',
102
195
  route: '/foo',
103
196
  status_code: 200,
104
- start_time: Time.new(2018, 1, 1, 0, 0, 50, 0)
105
- )
197
+ timing: 123,
198
+ ),
106
199
  )
200
+ subject.close
201
+
107
202
  expect(
108
- a_request(:put, routes).with(body: /"count":2/)
203
+ a_request(:put, routes).with(body: /"count":2/),
109
204
  ).to have_been_made
110
205
  end
111
206
 
@@ -115,19 +210,21 @@ RSpec.describe Airbrake::PerformanceNotifier do
115
210
  method: 'GET',
116
211
  route: '/foo',
117
212
  status_code: 200,
118
- start_time: Time.new(2018, 1, 1, 0, 0, 49, 0),
119
- end_time: Time.new(2018, 1, 1, 0, 0, 50, 0)
120
- )
213
+ timing: 1000,
214
+ time: Time.new(2018, 1, 1, 0, 0, 49, 0),
215
+ ),
121
216
  )
122
217
  subject.notify(
123
218
  Airbrake::Request.new(
124
219
  method: 'GET',
125
220
  route: '/foo',
126
221
  status_code: 200,
127
- start_time: Time.new(2018, 1, 1, 0, 1, 49, 0),
128
- end_time: Time.new(2018, 1, 1, 0, 1, 55, 0)
129
- )
222
+ timing: 6000,
223
+ time: Time.new(2018, 1, 1, 0, 1, 49, 0),
224
+ ),
130
225
  )
226
+ subject.close
227
+
131
228
  expect(
132
229
  a_request(:put, routes).with(
133
230
  body: %r|\A
@@ -138,8 +235,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
138
235
  {"method":"GET","route":"/foo","statusCode":200,
139
236
  "time":"2018-01-01T00:01:00\+00:00","count":1,"sum":6000.0,
140
237
  "sumsq":36000000.0,"tdigest":"AAAAAkA0AAAAAAAAAAAAAUW7gAAB"}\]}
141
- \z|x
142
- )
238
+ \z|x,
239
+ ),
143
240
  ).to have_been_made
144
241
  end
145
242
 
@@ -149,19 +246,21 @@ RSpec.describe Airbrake::PerformanceNotifier do
149
246
  method: 'GET',
150
247
  route: '/foo',
151
248
  status_code: 200,
152
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
153
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
154
- )
249
+ timing: 60000,
250
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
251
+ ),
155
252
  )
156
253
  subject.notify(
157
254
  Airbrake::Request.new(
158
255
  method: 'POST',
159
256
  route: '/foo',
160
257
  status_code: 200,
161
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
162
- end_time: Time.new(2018, 1, 1, 0, 50, 0, 0)
163
- )
258
+ timing: 60000,
259
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
260
+ ),
164
261
  )
262
+ subject.close
263
+
165
264
  expect(
166
265
  a_request(:put, routes).with(
167
266
  body: %r|\A
@@ -172,52 +271,135 @@ RSpec.describe Airbrake::PerformanceNotifier do
172
271
  {"method":"POST","route":"/foo","statusCode":200,
173
272
  "time":"2018-01-01T00:49:00\+00:00","count":1,"sum":60000.0,
174
273
  "sumsq":3600000000.0,"tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAB"}\]}
175
- \z|x
176
- )
274
+ \z|x,
275
+ ),
177
276
  ).to have_been_made
178
277
  end
179
278
 
180
- it "returns a promise" do
181
- promise = subject.notify(
182
- Airbrake::Request.new(
183
- method: 'GET',
184
- route: '/foo',
185
- status_code: 200,
186
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
187
- )
279
+ it "groups performance breakdowns by route key" do
280
+ subject.notify(
281
+ Airbrake::PerformanceBreakdown.new(
282
+ method: 'DELETE',
283
+ route: '/routes-breakdowns',
284
+ response_type: 'json',
285
+ timing: 2000,
286
+ time: Time.new(2018, 1, 1, 0, 0, 20, 0),
287
+ groups: { db: 131, view: 421 },
288
+ ),
188
289
  )
189
- expect(promise).to be_an(Airbrake::Promise)
190
- expect(promise.value).to eq('' => nil)
191
- end
192
-
193
- it "doesn't send route stats when performance stats are disabled" do
194
- Airbrake::Config.instance.merge(performance_stats: false)
195
-
196
- promise = subject.notify(
197
- Airbrake::Request.new(
198
- method: 'GET', route: '/foo', status_code: 200, start_time: Time.new
199
- )
290
+ subject.notify(
291
+ Airbrake::PerformanceBreakdown.new(
292
+ method: 'DELETE',
293
+ route: '/routes-breakdowns',
294
+ response_type: 'json',
295
+ timing: 2000,
296
+ time: Time.new(2018, 1, 1, 0, 0, 30, 0),
297
+ groups: { db: 55, view: 11 },
298
+ ),
200
299
  )
300
+ subject.close
201
301
 
202
- expect(a_request(:put, routes)).not_to have_been_made
203
- expect(promise.value).to eq(
204
- 'error' => "The Performance Stats feature is disabled"
205
- )
302
+ expect(
303
+ a_request(:put, breakdowns).with(body: %r|
304
+ \A{"routes":\[{
305
+ "method":"DELETE",
306
+ "route":"/routes-breakdowns",
307
+ "responseType":"json",
308
+ "time":"2018-01-01T00:00:00\+00:00",
309
+ "count":2,
310
+ "sum":4000.0,
311
+ "sumsq":8000000.0,
312
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUT6AAAC",
313
+ "groups":{
314
+ "db":{
315
+ "count":2,
316
+ "sum":186.0,
317
+ "sumsq":20186.0,
318
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAkJcAABCmAAAAQE="
319
+ },
320
+ "view":{
321
+ "count":2,
322
+ "sum":432.0,
323
+ "sumsq":177362.0,
324
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAkEwAABDzQAAAQE="
325
+ }
326
+ }
327
+ }\]}\z|x),
328
+ ).to have_been_made
206
329
  end
207
330
 
208
- it "doesn't send route stats when current environment is ignored" do
209
- Airbrake::Config.instance.merge(
210
- performance_stats: true, environment: 'test', ignore_environments: %w[test]
331
+ it "groups queues by queue key" do
332
+ subject.notify(
333
+ Airbrake::Queue.new(
334
+ queue: 'emails',
335
+ error_count: 2,
336
+ groups: { redis: 131, sql: 421 },
337
+ timing: 60000,
338
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
339
+ ),
340
+ )
341
+ subject.notify(
342
+ Airbrake::Queue.new(
343
+ queue: 'emails',
344
+ error_count: 3,
345
+ groups: { redis: 131, sql: 421 },
346
+ timing: 60000,
347
+ time: Time.new(2018, 1, 1, 0, 49, 0, 0),
348
+ ),
211
349
  )
350
+ subject.close
212
351
 
352
+ expect(
353
+ a_request(:put, queues).with(body: /
354
+ \A{"queues":\[{
355
+ "queue":"emails",
356
+ "errorCount":5,
357
+ "time":"2018-01-01T00:49:00\+00:00",
358
+ "count":2,
359
+ "sum":120000.0,
360
+ "sumsq":7200000000.0,
361
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUdqYAAC",
362
+ "groups":{
363
+ "redis":{
364
+ "count":2,
365
+ "sum":262.0,
366
+ "sumsq":34322.0,
367
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUMDAAAC"
368
+ },
369
+ "sql":{
370
+ "count":2,
371
+ "sum":842.0,
372
+ "sumsq":354482.0,
373
+ "tdigest":"AAAAAkA0AAAAAAAAAAAAAUPSgAAC"
374
+ }
375
+ }
376
+ }\]}\z/x),
377
+ ).to have_been_made
378
+ end
379
+
380
+ it "returns a promise" do
213
381
  promise = subject.notify(
214
382
  Airbrake::Request.new(
215
- method: 'GET', route: '/foo', status_code: 200, start_time: Time.new
216
- )
383
+ method: 'GET',
384
+ route: '/foo',
385
+ status_code: 200,
386
+ timing: 123,
387
+ ),
217
388
  )
389
+ subject.close
390
+
391
+ expect(promise).to be_an(Airbrake::Promise)
392
+ expect(promise.value).to eq('' => '')
393
+ end
218
394
 
219
- expect(a_request(:put, routes)).not_to have_been_made
220
- expect(promise.value).to eq('error' => "current environment 'test' is ignored")
395
+ it "checks performance stat configuration" do
396
+ request = Airbrake::Request.new(
397
+ method: 'GET', route: '/foo', status_code: 200, timing: 123,
398
+ )
399
+ expect(Airbrake::Config.instance).to receive(:check_performance_options)
400
+ .with(request).and_return(Airbrake::Promise.new)
401
+ subject.notify(request)
402
+ subject.close
221
403
  end
222
404
 
223
405
  it "sends environment when it's specified" do
@@ -228,13 +410,15 @@ RSpec.describe Airbrake::PerformanceNotifier do
228
410
  method: 'POST',
229
411
  route: '/foo',
230
412
  status_code: 200,
231
- start_time: Time.new
232
- )
413
+ timing: 123,
414
+ ),
233
415
  )
416
+ subject.close
417
+
234
418
  expect(
235
419
  a_request(:put, routes).with(
236
- body: /\A{"routes":\[.+\],"environment":"test"}\z/x
237
- )
420
+ body: /\A{"routes":\[.+\],"environment":"test"}\z/x,
421
+ ),
238
422
  ).to have_been_made
239
423
  end
240
424
 
@@ -255,7 +439,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
255
439
  project_id: 1,
256
440
  project_key: 'banana',
257
441
  performance_stats: true,
258
- performance_stats_flush_period: flush_period
442
+ performance_stats_flush_period: flush_period,
259
443
  )
260
444
 
261
445
  subject.notify(
@@ -263,8 +447,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
263
447
  method: 'GET',
264
448
  route: '/foo',
265
449
  status_code: 200,
266
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
267
- )
450
+ timing: 123,
451
+ ),
268
452
  )
269
453
 
270
454
  subject.notify(
@@ -272,8 +456,8 @@ RSpec.describe Airbrake::PerformanceNotifier do
272
456
  method: 'POST',
273
457
  route: '/foo',
274
458
  query: 'SELECT * FROM things',
275
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
276
- )
459
+ timing: 123,
460
+ ),
277
461
  )
278
462
 
279
463
  sleep(flush_period + 0.5)
@@ -292,9 +476,11 @@ RSpec.describe Airbrake::PerformanceNotifier do
292
476
  method: 'GET',
293
477
  route: '/foo',
294
478
  status_code: 200,
295
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
296
- )
479
+ timing: 1,
480
+ ),
297
481
  )
482
+ subject.close
483
+
298
484
  expect(a_request(:put, routes)).not_to have_been_made
299
485
  end
300
486
 
@@ -304,11 +490,29 @@ RSpec.describe Airbrake::PerformanceNotifier do
304
490
  method: 'POST',
305
491
  route: '/foo',
306
492
  query: 'SELECT * FROM things',
307
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
308
- )
493
+ timing: 1,
494
+ ),
309
495
  )
496
+ subject.close
497
+
310
498
  expect(a_request(:put, queries)).not_to have_been_made
311
499
  end
500
+
501
+ it "returns a rejected promise" do
502
+ promise = subject.notify(
503
+ Airbrake::Query.new(
504
+ method: 'POST',
505
+ route: '/foo',
506
+ query: 'SELECT * FROM things',
507
+ timing: 1,
508
+ ),
509
+ )
510
+ subject.close
511
+
512
+ expect(promise.value).to eq(
513
+ 'error' => 'Airbrake::Query was ignored by a filter',
514
+ )
515
+ end
312
516
  end
313
517
 
314
518
  context "when a filter that modifies payload was defined" do
@@ -324,24 +528,93 @@ RSpec.describe Airbrake::PerformanceNotifier do
324
528
  method: 'POST',
325
529
  route: '/foo',
326
530
  query: 'SELECT * FROM things',
327
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
328
- )
531
+ timing: 123,
532
+ ),
329
533
  )
534
+ subject.close
535
+
330
536
  expect(
331
537
  a_request(:put, queries).with(
332
- body: /\A{"queries":\[{"method":"POST","route":"\[Filtered\]"/
333
- )
538
+ body: /\A{"queries":\[{"method":"POST","route":"\[Filtered\]"/,
539
+ ),
334
540
  ).to have_been_made
335
541
  end
336
542
  end
543
+
544
+ context "when provided :timing is zero" do
545
+ it "doesn't notify" do
546
+ queue = Airbrake::Queue.new(queue: 'bananas', error_count: 0, timing: 0)
547
+ subject.notify(queue)
548
+ subject.close
549
+
550
+ expect(a_request(:put, queues)).not_to have_been_made
551
+ end
552
+
553
+ it "returns a rejected promise" do
554
+ queue = Airbrake::Queue.new(queue: 'bananas', error_count: 0, timing: 0)
555
+ promise = subject.notify(queue)
556
+ subject.close
557
+
558
+ expect(promise.value).to eq('error' => ':timing cannot be zero')
559
+ end
560
+ end
561
+ end
562
+
563
+ describe "#notify_sync" do
564
+ it "notifies synchronously" do
565
+ retval = subject.notify_sync(
566
+ Airbrake::Query.new(
567
+ method: 'POST',
568
+ route: '/foo',
569
+ query: 'SELECT * FROM things',
570
+ timing: 123,
571
+ ),
572
+ )
573
+
574
+ expect(
575
+ a_request(:put, queries).with(
576
+ body: %r|\A{"queries":\[{"method":"POST","route":"/foo"|,
577
+ ),
578
+ ).to have_been_made
579
+ expect(retval).to eq('' => '')
580
+ end
581
+ end
582
+
583
+ describe "#close" do
584
+ before do
585
+ Airbrake::Config.instance.merge(performance_stats_flush_period: 0.1)
586
+ end
587
+
588
+ after do
589
+ Airbrake::Config.instance.merge(performance_stats_flush_period: 0)
590
+ end
591
+
592
+ it "kills the background thread" do
593
+ expect_any_instance_of(Thread).to receive(:kill).and_call_original
594
+ subject.notify(
595
+ Airbrake::Query.new(
596
+ method: 'POST',
597
+ route: '/foo',
598
+ query: 'SELECT * FROM things',
599
+ timing: 123,
600
+ ),
601
+ )
602
+ subject.close
603
+ end
604
+
605
+ it "logs the exit message" do
606
+ allow(Airbrake::Loggable.instance).to receive(:debug)
607
+ expect(Airbrake::Loggable.instance).to receive(:debug).with(
608
+ /performance notifier closed/,
609
+ )
610
+ subject.close
611
+ end
337
612
  end
338
613
 
339
614
  describe "#delete_filter" do
340
615
  let(:filter) do
341
616
  Class.new do
342
- def call(resource)
343
- resource.ignore!
344
- end
617
+ def call(resource); end
345
618
  end
346
619
  end
347
620
 
@@ -354,9 +627,11 @@ RSpec.describe Airbrake::PerformanceNotifier do
354
627
  method: 'POST',
355
628
  route: '/foo',
356
629
  status_code: 200,
357
- start_time: Time.new(2018, 1, 1, 0, 49, 0, 0)
358
- )
630
+ timing: 123,
631
+ ),
359
632
  )
633
+ subject.close
634
+
360
635
  expect(a_request(:put, routes)).to have_been_made
361
636
  end
362
637
  end
@@ -0,0 +1,11 @@
1
+ RSpec.describe Airbrake::Query do
2
+ describe "#stash" do
3
+ subject do
4
+ described_class.new(
5
+ method: 'GET', route: '/', query: '',
6
+ )
7
+ end
8
+
9
+ it { is_expected.to respond_to(:stash) }
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ RSpec.describe Airbrake::Queue do
2
+ subject { described_class.new(queue: 'bananas', error_count: 0) }
3
+
4
+ describe "#ignore" do
5
+ it { is_expected.to respond_to(:ignore!) }
6
+ end
7
+
8
+ describe "#stash" do
9
+ it { is_expected.to respond_to(:stash) }
10
+ end
11
+
12
+ describe "#route" do
13
+ it "always returns an empty route" do
14
+ queue = described_class.new(queue: 'a', error_count: 0)
15
+ expect(queue.route).to be_empty
16
+ end
17
+ end
18
+ end