airbrake-ruby 4.15.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby/async_sender.rb +4 -2
  3. data/lib/airbrake-ruby/backtrace.rb +6 -5
  4. data/lib/airbrake-ruby/config/processor.rb +77 -0
  5. data/lib/airbrake-ruby/config/validator.rb +6 -0
  6. data/lib/airbrake-ruby/config.rb +44 -35
  7. data/lib/airbrake-ruby/context.rb +51 -0
  8. data/lib/airbrake-ruby/file_cache.rb +1 -1
  9. data/lib/airbrake-ruby/filter_chain.rb +3 -0
  10. data/lib/airbrake-ruby/filters/context_filter.rb +4 -5
  11. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  12. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +1 -1
  13. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  14. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +3 -4
  15. data/lib/airbrake-ruby/filters/git_repository_filter.rb +11 -2
  16. data/lib/airbrake-ruby/filters/git_revision_filter.rb +3 -1
  17. data/lib/airbrake-ruby/filters/keys_filter.rb +23 -15
  18. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  19. data/lib/airbrake-ruby/filters/sql_filter.rb +11 -11
  20. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  21. data/lib/airbrake-ruby/filters/thread_filter.rb +4 -3
  22. data/lib/airbrake-ruby/grouppable.rb +1 -1
  23. data/lib/airbrake-ruby/ignorable.rb +1 -2
  24. data/lib/airbrake-ruby/mergeable.rb +1 -1
  25. data/lib/airbrake-ruby/monotonic_time.rb +1 -1
  26. data/lib/airbrake-ruby/notice.rb +1 -8
  27. data/lib/airbrake-ruby/notice_notifier.rb +4 -4
  28. data/lib/airbrake-ruby/performance_breakdown.rb +1 -6
  29. data/lib/airbrake-ruby/performance_notifier.rb +40 -54
  30. data/lib/airbrake-ruby/promise.rb +1 -0
  31. data/lib/airbrake-ruby/query.rb +1 -6
  32. data/lib/airbrake-ruby/queue.rb +1 -8
  33. data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
  34. data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
  35. data/lib/airbrake-ruby/remote_settings.rb +128 -0
  36. data/lib/airbrake-ruby/request.rb +1 -8
  37. data/lib/airbrake-ruby/stat.rb +2 -13
  38. data/lib/airbrake-ruby/sync_sender.rb +3 -2
  39. data/lib/airbrake-ruby/tdigest.rb +12 -9
  40. data/lib/airbrake-ruby/thread_pool.rb +9 -6
  41. data/lib/airbrake-ruby/time_truncate.rb +2 -2
  42. data/lib/airbrake-ruby/timed_trace.rb +1 -3
  43. data/lib/airbrake-ruby/truncator.rb +8 -2
  44. data/lib/airbrake-ruby/version.rb +11 -1
  45. data/lib/airbrake-ruby.rb +44 -54
  46. data/spec/airbrake_spec.rb +178 -92
  47. data/spec/async_sender_spec.rb +10 -8
  48. data/spec/backtrace_spec.rb +39 -36
  49. data/spec/benchmark_spec.rb +7 -5
  50. data/spec/code_hunk_spec.rb +26 -17
  51. data/spec/config/processor_spec.rb +167 -0
  52. data/spec/config/validator_spec.rb +23 -3
  53. data/spec/config_spec.rb +43 -55
  54. data/spec/context_spec.rb +54 -0
  55. data/spec/deploy_notifier_spec.rb +6 -4
  56. data/spec/file_cache_spec.rb +1 -0
  57. data/spec/filter_chain_spec.rb +29 -24
  58. data/spec/filters/context_filter_spec.rb +14 -5
  59. data/spec/filters/dependency_filter_spec.rb +3 -1
  60. data/spec/filters/exception_attributes_filter_spec.rb +5 -3
  61. data/spec/filters/gem_root_filter_spec.rb +9 -6
  62. data/spec/filters/git_last_checkout_filter_spec.rb +10 -12
  63. data/spec/filters/{git_repository_filter.rb → git_repository_filter_spec.rb} +26 -15
  64. data/spec/filters/git_revision_filter_spec.rb +20 -20
  65. data/spec/filters/keys_allowlist_spec.rb +26 -16
  66. data/spec/filters/keys_blocklist_spec.rb +35 -18
  67. data/spec/filters/root_directory_filter_spec.rb +7 -7
  68. data/spec/filters/sql_filter_spec.rb +28 -28
  69. data/spec/filters/system_exit_filter_spec.rb +4 -2
  70. data/spec/filters/thread_filter_spec.rb +16 -14
  71. data/spec/loggable_spec.rb +2 -2
  72. data/spec/monotonic_time_spec.rb +8 -6
  73. data/spec/nested_exception_spec.rb +46 -46
  74. data/spec/notice_notifier/options_spec.rb +25 -15
  75. data/spec/notice_notifier_spec.rb +54 -49
  76. data/spec/notice_spec.rb +7 -3
  77. data/spec/performance_breakdown_spec.rb +0 -12
  78. data/spec/performance_notifier_spec.rb +69 -87
  79. data/spec/promise_spec.rb +38 -32
  80. data/spec/query_spec.rb +1 -11
  81. data/spec/queue_spec.rb +1 -13
  82. data/spec/remote_settings/callback_spec.rb +162 -0
  83. data/spec/remote_settings/settings_data_spec.rb +348 -0
  84. data/spec/remote_settings_spec.rb +201 -0
  85. data/spec/request_spec.rb +1 -13
  86. data/spec/response_spec.rb +34 -12
  87. data/spec/spec_helper.rb +4 -4
  88. data/spec/stashable_spec.rb +5 -5
  89. data/spec/stat_spec.rb +7 -14
  90. data/spec/sync_sender_spec.rb +52 -17
  91. data/spec/tdigest_spec.rb +61 -56
  92. data/spec/thread_pool_spec.rb +67 -58
  93. data/spec/time_truncate_spec.rb +23 -6
  94. data/spec/timed_trace_spec.rb +32 -30
  95. data/spec/truncator_spec.rb +72 -43
  96. metadata +67 -51
@@ -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,
@@ -10,29 +12,20 @@ RSpec.describe Airbrake::Stat do
10
12
  end
11
13
  end
12
14
 
13
- describe "#increment" do
14
- let(:start_time) { Time.new(2018, 1, 1, 0, 0, 20, 0) }
15
- let(:end_time) { Time.new(2018, 1, 1, 0, 0, 22, 0) }
16
-
17
- before { subject.increment(start_time, end_time) }
18
-
19
- its(:sum) { is_expected.to eq(2000) }
20
- end
21
-
22
15
  describe "#increment_ms" do
23
- before { subject.increment_ms(1000) }
16
+ before { stat.increment_ms(1000) }
24
17
 
25
18
  its(:sum) { is_expected.to eq(1000) }
26
19
  its(:sumsq) { is_expected.to eq(1000000) }
27
20
 
28
21
  it "updates tdigest" do
29
- expect(subject.tdigest.size).to eq(1)
22
+ expect(stat.tdigest.size).to eq(1)
30
23
  end
31
24
  end
32
25
 
33
26
  describe "#inspect" do
34
27
  it "provides custom inspect output" do
35
- expect(subject.inspect).to eq(
28
+ expect(stat.inspect).to eq(
36
29
  '#<struct Airbrake::Stat count=0, sum=0.0, sumsq=0.0>',
37
30
  )
38
31
  end
@@ -40,7 +33,7 @@ RSpec.describe Airbrake::Stat do
40
33
 
41
34
  describe "#pretty_print" do
42
35
  it "is an alias of #inspect" do
43
- expect(subject.method(:pretty_print)).to eql(subject.method(:inspect))
36
+ expect(stat.method(:pretty_print)).to eql(stat.method(:inspect))
44
37
  end
45
38
  end
46
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,18 +25,20 @@ 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: {
30
- 'User-Agent' => %r{airbrake-ruby/\d+\.\d+\.\d+ Ruby/\d+\.\d+\.\d+},
32
+ 'User-Agent' => %r{
33
+ airbrake-ruby/\d+\.\d+\.\d+(\.rc\.\d+)?\sRuby/\d+\.\d+\.\d+
34
+ }x,
31
35
  },
32
36
  ),
33
37
  ).to have_been_made.once
34
38
  end
35
39
 
36
40
  it "sets the Authorization header to the project key" do
37
- subject.send({}, promise)
41
+ sync_sender.send({}, promise)
38
42
  expect(
39
43
  a_request(:post, endpoint).with(
40
44
  headers: { 'Authorization' => 'Bearer banana' },
@@ -43,19 +47,46 @@ RSpec.describe Airbrake::SyncSender do
43
47
  end
44
48
 
45
49
  it "catches exceptions raised while sending" do
50
+ # rubocop:disable RSpec/VerifiedDoubles
46
51
  https = double("foo")
47
- 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
+
48
58
  allow(https).to receive(:request).and_raise(StandardError.new('foo'))
49
- 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(
50
80
  /HTTP error: foo/,
51
81
  )
52
- expect(subject.send({}, promise)).to be_an(Airbrake::Promise)
53
- expect(promise.value).to eq('error' => '**Airbrake: HTTP error: foo')
54
82
  end
55
83
 
56
84
  context "when request body is nil" do
85
+ # rubocop:disable RSpec/MultipleExpectations
57
86
  it "doesn't send data" do
58
- expect_any_instance_of(Airbrake::Truncator)
87
+ allow(Airbrake::Loggable.instance).to receive(:error)
88
+
89
+ allow_any_instance_of(Airbrake::Truncator)
59
90
  .to receive(:reduce_max_size).and_return(0)
60
91
 
61
92
  encoded = Base64.encode64("\xD3\xE6\xBC\x9D\xBA").encode!('ASCII-8BIT')
@@ -68,16 +99,18 @@ RSpec.describe Airbrake::SyncSender do
68
99
 
69
100
  notice = Airbrake::Notice.new(ex)
70
101
 
71
- 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(
72
107
  /data was not sent/,
73
108
  )
74
- expect(Airbrake::Loggable.instance).to receive(:error).with(
109
+ expect(Airbrake::Loggable.instance).to have_received(:error).with(
75
110
  /truncation failed/,
76
111
  )
77
- expect(subject.send(notice, promise)).to be_an(Airbrake::Promise)
78
- expect(promise.value)
79
- .to match('error' => '**Airbrake: data was not sent because of missing body')
80
112
  end
113
+ # rubocop:enable RSpec/MultipleExpectations
81
114
  end
82
115
 
83
116
  context "when IP is rate limited" do
@@ -91,13 +124,14 @@ RSpec.describe Airbrake::SyncSender do
91
124
  )
92
125
  end
93
126
 
127
+ # rubocop:disable RSpec/MultipleExpectations
94
128
  it "returns error" do
95
129
  p1 = Airbrake::Promise.new
96
- subject.send({}, p1)
130
+ sync_sender.send({}, p1)
97
131
  expect(p1.value).to match('error' => '**Airbrake: IP is rate limited')
98
132
 
99
133
  p2 = Airbrake::Promise.new
100
- subject.send({}, p2)
134
+ sync_sender.send({}, p2)
101
135
  expect(p2.value).to match('error' => '**Airbrake: IP is rate limited')
102
136
 
103
137
  # Wait for X-RateLimit-Delay and then make a new request to make sure p2
@@ -105,11 +139,12 @@ RSpec.describe Airbrake::SyncSender do
105
139
  sleep 1
106
140
 
107
141
  p3 = Airbrake::Promise.new
108
- subject.send({}, p3)
142
+ sync_sender.send({}, p3)
109
143
  expect(p3.value).to match('error' => '**Airbrake: IP is rate limited')
110
144
 
111
145
  expect(a_request(:post, endpoint)).to have_been_made.twice
112
146
  end
147
+ # rubocop:enable RSpec/MultipleExpectations
113
148
  end
114
149
 
115
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,76 +26,75 @@ 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
 
89
- expect(maxerr).to be < 0.01
90
+ expect(maxerr).to be < 0.02
90
91
  end
91
92
  end
92
93
  end
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