airbrake-ruby 4.1.0 → 5.0.0

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