airbrake-ruby 5.2.0-java → 5.2.1-java

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +3 -2
  3. data/lib/airbrake-ruby/async_sender.rb +3 -1
  4. data/lib/airbrake-ruby/context.rb +51 -0
  5. data/lib/airbrake-ruby/filter_chain.rb +2 -0
  6. data/lib/airbrake-ruby/filters/context_filter.rb +4 -5
  7. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +1 -1
  8. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +1 -1
  9. data/lib/airbrake-ruby/filters/git_revision_filter.rb +1 -1
  10. data/lib/airbrake-ruby/filters/keys_filter.rb +2 -2
  11. data/lib/airbrake-ruby/filters/sql_filter.rb +2 -2
  12. data/lib/airbrake-ruby/filters/thread_filter.rb +1 -1
  13. data/lib/airbrake-ruby/ignorable.rb +0 -2
  14. data/lib/airbrake-ruby/notice_notifier.rb +3 -4
  15. data/lib/airbrake-ruby/performance_notifier.rb +1 -2
  16. data/lib/airbrake-ruby/remote_settings/settings_data.rb +1 -1
  17. data/lib/airbrake-ruby/tdigest.rb +7 -6
  18. data/lib/airbrake-ruby/thread_pool.rb +5 -3
  19. data/lib/airbrake-ruby/timed_trace.rb +1 -3
  20. data/lib/airbrake-ruby/version.rb +1 -1
  21. data/spec/airbrake_spec.rb +139 -76
  22. data/spec/async_sender_spec.rb +10 -8
  23. data/spec/backtrace_spec.rb +13 -10
  24. data/spec/benchmark_spec.rb +5 -3
  25. data/spec/code_hunk_spec.rb +24 -15
  26. data/spec/config/processor_spec.rb +12 -4
  27. data/spec/config/validator_spec.rb +5 -2
  28. data/spec/config_spec.rb +24 -16
  29. data/spec/context_spec.rb +54 -0
  30. data/spec/deploy_notifier_spec.rb +6 -4
  31. data/spec/file_cache_spec.rb +1 -0
  32. data/spec/filter_chain_spec.rb +29 -24
  33. data/spec/filters/context_filter_spec.rb +14 -5
  34. data/spec/filters/dependency_filter_spec.rb +3 -1
  35. data/spec/filters/exception_attributes_filter_spec.rb +5 -3
  36. data/spec/filters/gem_root_filter_spec.rb +5 -2
  37. data/spec/filters/git_last_checkout_filter_spec.rb +10 -12
  38. data/spec/filters/git_repository_filter.rb +9 -9
  39. data/spec/filters/git_revision_filter_spec.rb +19 -19
  40. data/spec/filters/keys_allowlist_spec.rb +25 -16
  41. data/spec/filters/keys_blocklist_spec.rb +25 -18
  42. data/spec/filters/root_directory_filter_spec.rb +3 -3
  43. data/spec/filters/sql_filter_spec.rb +26 -26
  44. data/spec/filters/system_exit_filter_spec.rb +4 -2
  45. data/spec/filters/thread_filter_spec.rb +15 -13
  46. data/spec/loggable_spec.rb +2 -2
  47. data/spec/monotonic_time_spec.rb +8 -6
  48. data/spec/nested_exception_spec.rb +46 -46
  49. data/spec/notice_notifier/options_spec.rb +23 -13
  50. data/spec/notice_notifier_spec.rb +52 -47
  51. data/spec/notice_spec.rb +6 -2
  52. data/spec/performance_notifier_spec.rb +67 -60
  53. data/spec/promise_spec.rb +38 -32
  54. data/spec/remote_settings/callback_spec.rb +27 -8
  55. data/spec/remote_settings/settings_data_spec.rb +4 -4
  56. data/spec/remote_settings_spec.rb +18 -8
  57. data/spec/response_spec.rb +34 -12
  58. data/spec/stashable_spec.rb +5 -5
  59. data/spec/stat_spec.rb +7 -5
  60. data/spec/sync_sender_spec.rb +49 -16
  61. data/spec/tdigest_spec.rb +60 -55
  62. data/spec/thread_pool_spec.rb +65 -55
  63. data/spec/time_truncate_spec.rb +4 -2
  64. data/spec/timed_trace_spec.rb +32 -30
  65. data/spec/truncator_spec.rb +72 -43
  66. metadata +51 -48
@@ -5,19 +5,19 @@ RSpec.describe Airbrake::Stashable do
5
5
  end
6
6
 
7
7
  describe "#stash" do
8
- subject { klass.new }
8
+ subject(:instance) { klass.new }
9
9
 
10
10
  it "returns a hash" do
11
- expect(subject.stash).to be_a(Hash)
11
+ expect(instance.stash).to be_a(Hash)
12
12
  end
13
13
 
14
14
  it "returns an empty hash" do
15
- expect(subject.stash).to be_empty
15
+ expect(instance.stash).to be_empty
16
16
  end
17
17
 
18
18
  it "remembers what was put in the stash" do
19
- subject.stash[:foo] = 1
20
- expect(subject.stash[:foo]).to eq(1)
19
+ instance.stash[:foo] = 1
20
+ expect(instance.stash[:foo]).to eq(1)
21
21
  end
22
22
  end
23
23
  end
data/spec/stat_spec.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  RSpec.describe Airbrake::Stat do
2
+ subject(:stat) { described_class.new }
3
+
2
4
  describe "#to_h" do
3
5
  it "converts to a hash" do
4
- expect(subject.to_h).to eq(
6
+ expect(stat.to_h).to eq(
5
7
  'count' => 0,
6
8
  'sum' => 0.0,
7
9
  'sumsq' => 0.0,
@@ -11,19 +13,19 @@ RSpec.describe Airbrake::Stat do
11
13
  end
12
14
 
13
15
  describe "#increment_ms" do
14
- before { subject.increment_ms(1000) }
16
+ before { stat.increment_ms(1000) }
15
17
 
16
18
  its(:sum) { is_expected.to eq(1000) }
17
19
  its(:sumsq) { is_expected.to eq(1000000) }
18
20
 
19
21
  it "updates tdigest" do
20
- expect(subject.tdigest.size).to eq(1)
22
+ expect(stat.tdigest.size).to eq(1)
21
23
  end
22
24
  end
23
25
 
24
26
  describe "#inspect" do
25
27
  it "provides custom inspect output" do
26
- expect(subject.inspect).to eq(
28
+ expect(stat.inspect).to eq(
27
29
  '#<struct Airbrake::Stat count=0, sum=0.0, sumsq=0.0>',
28
30
  )
29
31
  end
@@ -31,7 +33,7 @@ RSpec.describe Airbrake::Stat do
31
33
 
32
34
  describe "#pretty_print" do
33
35
  it "is an alias of #inspect" do
34
- expect(subject.method(:pretty_print)).to eql(subject.method(:inspect))
36
+ expect(stat.method(:pretty_print)).to eql(stat.method(:inspect))
35
37
  end
36
38
  end
37
39
  end
@@ -1,4 +1,6 @@
1
1
  RSpec.describe Airbrake::SyncSender do
2
+ subject(:sync_sender) { described_class.new }
3
+
2
4
  before do
3
5
  Airbrake::Config.instance = Airbrake::Config.new(
4
6
  project_id: 1, project_key: 'banana',
@@ -14,7 +16,7 @@ RSpec.describe Airbrake::SyncSender do
14
16
  before { stub_request(:post, endpoint).to_return(body: '{}') }
15
17
 
16
18
  it "sets the Content-Type header to JSON" do
17
- subject.send({}, promise)
19
+ sync_sender.send({}, promise)
18
20
  expect(
19
21
  a_request(:post, endpoint).with(
20
22
  headers: { 'Content-Type' => 'application/json' },
@@ -23,7 +25,7 @@ RSpec.describe Airbrake::SyncSender do
23
25
  end
24
26
 
25
27
  it "sets the User-Agent header to the notifier slug" do
26
- subject.send({}, promise)
28
+ sync_sender.send({}, promise)
27
29
  expect(
28
30
  a_request(:post, endpoint).with(
29
31
  headers: {
@@ -36,7 +38,7 @@ RSpec.describe Airbrake::SyncSender do
36
38
  end
37
39
 
38
40
  it "sets the Authorization header to the project key" do
39
- subject.send({}, promise)
41
+ sync_sender.send({}, promise)
40
42
  expect(
41
43
  a_request(:post, endpoint).with(
42
44
  headers: { 'Authorization' => 'Bearer banana' },
@@ -45,19 +47,46 @@ RSpec.describe Airbrake::SyncSender do
45
47
  end
46
48
 
47
49
  it "catches exceptions raised while sending" do
50
+ # rubocop:disable RSpec/VerifiedDoubles
48
51
  https = double("foo")
49
- allow(subject).to receive(:build_https).and_return(https)
52
+ # rubocop:enable RSpec/VerifiedDoubles
53
+
54
+ # rubocop:disable RSpec/SubjectStub
55
+ allow(sync_sender).to receive(:build_https).and_return(https)
56
+ # rubocop:enable RSpec/SubjectStub
57
+
50
58
  allow(https).to receive(:request).and_raise(StandardError.new('foo'))
51
- expect(Airbrake::Loggable.instance).to receive(:error).with(
59
+
60
+ expect(sync_sender.send({}, promise)).to be_an(Airbrake::Promise)
61
+ expect(promise.value).to eq('error' => '**Airbrake: HTTP error: foo')
62
+ end
63
+
64
+ it "logs exceptions raised while sending" do
65
+ allow(Airbrake::Loggable.instance).to receive(:error)
66
+
67
+ # rubocop:disable RSpec/VerifiedDoubles
68
+ https = double("foo")
69
+ # rubocop:enable RSpec/VerifiedDoubles
70
+
71
+ # rubocop:disable RSpec/SubjectStub
72
+ allow(sync_sender).to receive(:build_https).and_return(https)
73
+ # rubocop:enable RSpec/SubjectStub
74
+
75
+ allow(https).to receive(:request).and_raise(StandardError.new('foo'))
76
+
77
+ sync_sender.send({}, promise)
78
+
79
+ expect(Airbrake::Loggable.instance).to have_received(:error).with(
52
80
  /HTTP error: foo/,
53
81
  )
54
- expect(subject.send({}, promise)).to be_an(Airbrake::Promise)
55
- expect(promise.value).to eq('error' => '**Airbrake: HTTP error: foo')
56
82
  end
57
83
 
58
84
  context "when request body is nil" do
85
+ # rubocop:disable RSpec/MultipleExpectations
59
86
  it "doesn't send data" do
60
- expect_any_instance_of(Airbrake::Truncator)
87
+ allow(Airbrake::Loggable.instance).to receive(:error)
88
+
89
+ allow_any_instance_of(Airbrake::Truncator)
61
90
  .to receive(:reduce_max_size).and_return(0)
62
91
 
63
92
  encoded = Base64.encode64("\xD3\xE6\xBC\x9D\xBA").encode!('ASCII-8BIT')
@@ -70,16 +99,18 @@ RSpec.describe Airbrake::SyncSender do
70
99
 
71
100
  notice = Airbrake::Notice.new(ex)
72
101
 
73
- expect(Airbrake::Loggable.instance).to receive(:error).with(
102
+ expect(sync_sender.send(notice, promise)).to be_an(Airbrake::Promise)
103
+ expect(promise.value)
104
+ .to match('error' => '**Airbrake: data was not sent because of missing body')
105
+
106
+ expect(Airbrake::Loggable.instance).to have_received(:error).with(
74
107
  /data was not sent/,
75
108
  )
76
- expect(Airbrake::Loggable.instance).to receive(:error).with(
109
+ expect(Airbrake::Loggable.instance).to have_received(:error).with(
77
110
  /truncation failed/,
78
111
  )
79
- expect(subject.send(notice, promise)).to be_an(Airbrake::Promise)
80
- expect(promise.value)
81
- .to match('error' => '**Airbrake: data was not sent because of missing body')
82
112
  end
113
+ # rubocop:enable RSpec/MultipleExpectations
83
114
  end
84
115
 
85
116
  context "when IP is rate limited" do
@@ -93,13 +124,14 @@ RSpec.describe Airbrake::SyncSender do
93
124
  )
94
125
  end
95
126
 
127
+ # rubocop:disable RSpec/MultipleExpectations
96
128
  it "returns error" do
97
129
  p1 = Airbrake::Promise.new
98
- subject.send({}, p1)
130
+ sync_sender.send({}, p1)
99
131
  expect(p1.value).to match('error' => '**Airbrake: IP is rate limited')
100
132
 
101
133
  p2 = Airbrake::Promise.new
102
- subject.send({}, p2)
134
+ sync_sender.send({}, p2)
103
135
  expect(p2.value).to match('error' => '**Airbrake: IP is rate limited')
104
136
 
105
137
  # Wait for X-RateLimit-Delay and then make a new request to make sure p2
@@ -107,11 +139,12 @@ RSpec.describe Airbrake::SyncSender do
107
139
  sleep 1
108
140
 
109
141
  p3 = Airbrake::Promise.new
110
- subject.send({}, p3)
142
+ sync_sender.send({}, p3)
111
143
  expect(p3.value).to match('error' => '**Airbrake: IP is rate limited')
112
144
 
113
145
  expect(a_request(:post, endpoint)).to have_been_made.twice
114
146
  end
147
+ # rubocop:enable RSpec/MultipleExpectations
115
148
  end
116
149
 
117
150
  context "when the provided method is :put" do
data/spec/tdigest_spec.rb CHANGED
@@ -1,16 +1,18 @@
1
1
  RSpec.describe Airbrake::TDigest do
2
+ subject(:tdigest) { described_class.new }
3
+
2
4
  describe "byte serialization" do
3
5
  it "loads serialized data" do
4
- subject.push(60, 100)
5
- 10.times { subject.push(rand * 100) }
6
- bytes = subject.as_bytes
6
+ tdigest.push(60, 100)
7
+ 10.times { tdigest.push(rand * 100) }
8
+ bytes = tdigest.as_bytes
7
9
  new_tdigest = described_class.from_bytes(bytes)
8
- expect(new_tdigest.percentile(0.9)).to eq(subject.percentile(0.9))
10
+ expect(new_tdigest.percentile(0.9)).to eq(tdigest.percentile(0.9))
9
11
  expect(new_tdigest.as_bytes).to eq(bytes)
10
12
  end
11
13
 
12
14
  it "handles zero size" do
13
- bytes = subject.as_bytes
15
+ bytes = tdigest.as_bytes
14
16
  expect(described_class.from_bytes(bytes).size).to be_zero
15
17
  end
16
18
 
@@ -24,65 +26,64 @@ RSpec.describe Airbrake::TDigest do
24
26
 
25
27
  describe "small byte serialization" do
26
28
  it "loads serialized data" do
27
- 10.times { subject.push(10) }
28
- bytes = subject.as_small_bytes
29
+ 10.times { tdigest.push(10) }
30
+ bytes = tdigest.as_small_bytes
29
31
  new_tdigest = described_class.from_bytes(bytes)
30
32
  # Expect some rounding error due to compression
31
33
  expect(new_tdigest.percentile(0.9).round(5)).to eq(
32
- subject.percentile(0.9).round(5),
34
+ tdigest.percentile(0.9).round(5),
33
35
  )
34
36
  expect(new_tdigest.as_small_bytes).to eq(bytes)
35
37
  end
36
38
 
37
39
  it "handles zero size" do
38
- bytes = subject.as_small_bytes
40
+ bytes = tdigest.as_small_bytes
39
41
  expect(described_class.from_bytes(bytes).size).to be_zero
40
42
  end
41
43
  end
42
44
 
43
45
  describe "JSON serialization" do
44
46
  it "loads serialized data" do
45
- subject.push(60, 100)
46
- json = subject.as_json
47
+ tdigest.push(60, 100)
48
+ json = tdigest.as_json
47
49
  new_tdigest = described_class.from_json(json)
48
- expect(new_tdigest.percentile(0.9)).to eq(subject.percentile(0.9))
50
+ expect(new_tdigest.percentile(0.9)).to eq(tdigest.percentile(0.9))
49
51
  end
50
52
  end
51
53
 
52
54
  describe "#percentile" do
53
55
  it "returns nil if empty" do
54
- expect(subject.percentile(0.90)).to be_nil # This should not crash
56
+ expect(tdigest.percentile(0.90)).to be_nil # This should not crash
55
57
  end
56
58
 
57
59
  it "raises ArgumentError of input not between 0 and 1" do
58
- expect { subject.percentile(1.1) }.to raise_error(ArgumentError)
60
+ expect { tdigest.percentile(1.1) }.to raise_error(ArgumentError)
59
61
  end
60
62
 
61
63
  describe "with only single value" do
62
64
  it "returns the value" do
63
- subject.push(60, 100)
64
- expect(subject.percentile(0.90)).to eq(60)
65
+ tdigest.push(60, 100)
66
+ expect(tdigest.percentile(0.90)).to eq(60)
65
67
  end
66
68
 
67
69
  it "returns 0 for all percentiles when only 0 present" do
68
- subject.push(0)
69
- expect(subject.percentile([0.0, 0.5, 1.0])).to eq([0, 0, 0])
70
+ tdigest.push(0)
71
+ expect(tdigest.percentile([0.0, 0.5, 1.0])).to eq([0, 0, 0])
70
72
  end
71
73
  end
72
74
 
73
75
  describe "with alot of uniformly distributed points" do
74
76
  it "has minimal error" do
75
77
  seed = srand(1234) # Makes the values a proper fixture
76
- N = 100_000
77
78
  maxerr = 0
78
- values = Array.new(N).map { rand }
79
+ values = Array.new(100_000).map { rand }
79
80
  srand(seed)
80
81
 
81
- subject.push(values)
82
- subject.compress!
82
+ tdigest.push(values)
83
+ tdigest.compress!
83
84
 
84
85
  0.step(1, 0.1).each do |i|
85
- q = subject.percentile(i)
86
+ q = tdigest.percentile(i)
86
87
  maxerr = [maxerr, (i - q).abs].max
87
88
  end
88
89
 
@@ -93,7 +94,7 @@ RSpec.describe Airbrake::TDigest do
93
94
 
94
95
  describe "#push" do
95
96
  it "calls _cumulate so won't crash because of uninitialized mean_cumn" do
96
- subject.push(
97
+ tdigest.push(
97
98
  [
98
99
  125000000.0,
99
100
  104166666.66666666,
@@ -132,59 +133,62 @@ RSpec.describe Airbrake::TDigest do
132
133
  end
133
134
 
134
135
  it "does not blow up if data comes in sorted" do
135
- subject.push(0..10_000)
136
- expect(subject.centroids.size).to be < 5_000
137
- subject.compress!
138
- expect(subject.centroids.size).to be < 1_000
136
+ tdigest.push(0..10_000)
137
+ expect(tdigest.centroids.size).to be < 5_000
138
+ tdigest.compress!
139
+ expect(tdigest.centroids.size).to be < 1_000
139
140
  end
140
141
  end
141
142
 
142
143
  describe "#size" do
143
144
  it "reports the number of observations" do
144
145
  n = 10_000
145
- n.times { subject.push(rand) }
146
- subject.compress!
147
- expect(subject.size).to eq(n)
146
+ n.times { tdigest.push(rand) }
147
+ tdigest.compress!
148
+ expect(tdigest.size).to eq(n)
148
149
  end
149
150
  end
150
151
 
151
152
  describe "#+" do
152
153
  it "works with empty tdigests" do
153
154
  other = described_class.new(0.001, 50, 1.2)
154
- expect((subject + other).centroids.size).to eq(0)
155
+ expect((tdigest + other).centroids.size).to eq(0)
155
156
  end
156
157
 
157
158
  describe "adding two tdigests" do
159
+ let(:other) { described_class.new(0.001, 50, 1.2) }
160
+
158
161
  before do
159
- @other = described_class.new(0.001, 50, 1.2)
160
- [subject, @other].each do |td|
162
+ [tdigest, other].each do |td|
161
163
  td.push(60, 100)
162
164
  10.times { td.push(rand * 100) }
163
165
  end
164
166
  end
165
167
 
168
+ # rubocop:disable RSpec/MultipleExpectations
166
169
  it "has the parameters of the left argument (the calling tdigest)" do
167
- new_tdigest = subject + @other
170
+ new_tdigest = tdigest + other
168
171
  expect(new_tdigest.instance_variable_get(:@delta)).to eq(
169
- subject.instance_variable_get(:@delta),
172
+ tdigest.instance_variable_get(:@delta),
170
173
  )
171
174
  expect(new_tdigest.instance_variable_get(:@k)).to eq(
172
- subject.instance_variable_get(:@k),
175
+ tdigest.instance_variable_get(:@k),
173
176
  )
174
177
  expect(new_tdigest.instance_variable_get(:@cx)).to eq(
175
- subject.instance_variable_get(:@cx),
178
+ tdigest.instance_variable_get(:@cx),
176
179
  )
177
180
  end
181
+ # rubocop:enable RSpec/MultipleExpectations
178
182
 
179
183
  it "returns a tdigest with less than or equal centroids" do
180
- new_tdigest = subject + @other
184
+ new_tdigest = tdigest + other
181
185
  expect(new_tdigest.centroids.size)
182
- .to be <= subject.centroids.size + @other.centroids.size
186
+ .to be <= tdigest.centroids.size + other.centroids.size
183
187
  end
184
188
 
185
189
  it "has the size of the two digests combined" do
186
- new_tdigest = subject + @other
187
- expect(new_tdigest.size).to eq(subject.size + @other.size)
190
+ new_tdigest = tdigest + other
191
+ expect(new_tdigest.size).to eq(tdigest.size + other.size)
188
192
  end
189
193
  end
190
194
  end
@@ -192,14 +196,15 @@ RSpec.describe Airbrake::TDigest do
192
196
  describe "#merge!" do
193
197
  it "works with empty tdigests" do
194
198
  other = described_class.new(0.001, 50, 1.2)
195
- subject.merge!(other)
196
- expect(subject.centroids.size).to be_zero
199
+ tdigest.merge!(other)
200
+ expect(tdigest.centroids.size).to be_zero
197
201
  end
198
202
 
199
203
  describe "with populated tdigests" do
204
+ let(:other) { described_class.new(0.001, 50, 1.2) }
205
+
200
206
  before do
201
- @other = described_class.new(0.001, 50, 1.2)
202
- [subject, @other].each do |td|
207
+ [tdigest, other].each do |td|
203
208
  td.push(60, 100)
204
209
  10.times { td.push(rand * 100) }
205
210
  end
@@ -207,23 +212,23 @@ RSpec.describe Airbrake::TDigest do
207
212
 
208
213
  it "has the parameters of the calling tdigest" do
209
214
  vars = %i[@delta @k @cx]
210
- expected = Hash[vars.map { |v| [v, subject.instance_variable_get(v)] }]
211
- subject.merge!(@other)
215
+ expected = vars.map { |v| [v, tdigest.instance_variable_get(v)] }.to_h
216
+ tdigest.merge!(other)
212
217
  vars.each do |v|
213
- expect(subject.instance_variable_get(v)).to eq(expected[v])
218
+ expect(tdigest.instance_variable_get(v)).to eq(expected[v])
214
219
  end
215
220
  end
216
221
 
217
222
  it "returns a tdigest with less than or equal centroids" do
218
- combined_size = subject.centroids.size + @other.centroids.size
219
- subject.merge!(@other)
220
- expect(subject.centroids.size).to be <= combined_size
223
+ combined_size = tdigest.centroids.size + other.centroids.size
224
+ tdigest.merge!(other)
225
+ expect(tdigest.centroids.size).to be <= combined_size
221
226
  end
222
227
 
223
228
  it "has the size of the two digests combined" do
224
- combined_size = subject.size + @other.size
225
- subject.merge!(@other)
226
- expect(subject.size).to eq(combined_size)
229
+ combined_size = tdigest.size + other.size
230
+ tdigest.merge!(other)
231
+ expect(tdigest.size).to eq(combined_size)
227
232
  end
228
233
  end
229
234
  end