airbrake-ruby 4.8.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +132 -57
  3. data/lib/airbrake-ruby/async_sender.rb +7 -30
  4. data/lib/airbrake-ruby/backtrace.rb +8 -7
  5. data/lib/airbrake-ruby/benchmark.rb +1 -1
  6. data/lib/airbrake-ruby/code_hunk.rb +1 -1
  7. data/lib/airbrake-ruby/config.rb +59 -15
  8. data/lib/airbrake-ruby/config/processor.rb +71 -0
  9. data/lib/airbrake-ruby/config/validator.rb +9 -3
  10. data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
  11. data/lib/airbrake-ruby/file_cache.rb +1 -1
  12. data/lib/airbrake-ruby/filter_chain.rb +16 -1
  13. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  14. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
  15. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  16. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +5 -5
  17. data/lib/airbrake-ruby/filters/git_repository_filter.rb +3 -0
  18. data/lib/airbrake-ruby/filters/git_revision_filter.rb +2 -0
  19. data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
  20. data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
  21. data/lib/airbrake-ruby/filters/keys_filter.rb +39 -20
  22. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  23. data/lib/airbrake-ruby/filters/sql_filter.rb +7 -7
  24. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  25. data/lib/airbrake-ruby/filters/thread_filter.rb +5 -4
  26. data/lib/airbrake-ruby/grouppable.rb +12 -0
  27. data/lib/airbrake-ruby/ignorable.rb +1 -0
  28. data/lib/airbrake-ruby/inspectable.rb +2 -2
  29. data/lib/airbrake-ruby/loggable.rb +1 -1
  30. data/lib/airbrake-ruby/mergeable.rb +12 -0
  31. data/lib/airbrake-ruby/monotonic_time.rb +5 -0
  32. data/lib/airbrake-ruby/notice.rb +7 -14
  33. data/lib/airbrake-ruby/notice_notifier.rb +11 -3
  34. data/lib/airbrake-ruby/performance_breakdown.rb +16 -10
  35. data/lib/airbrake-ruby/performance_notifier.rb +80 -58
  36. data/lib/airbrake-ruby/promise.rb +1 -0
  37. data/lib/airbrake-ruby/query.rb +20 -15
  38. data/lib/airbrake-ruby/queue.rb +65 -0
  39. data/lib/airbrake-ruby/remote_settings.rb +105 -0
  40. data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
  41. data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
  42. data/lib/airbrake-ruby/request.rb +14 -12
  43. data/lib/airbrake-ruby/stat.rb +26 -33
  44. data/lib/airbrake-ruby/sync_sender.rb +3 -2
  45. data/lib/airbrake-ruby/tdigest.rb +43 -58
  46. data/lib/airbrake-ruby/thread_pool.rb +11 -1
  47. data/lib/airbrake-ruby/truncator.rb +10 -4
  48. data/lib/airbrake-ruby/version.rb +11 -1
  49. data/spec/airbrake_spec.rb +206 -71
  50. data/spec/async_sender_spec.rb +3 -12
  51. data/spec/backtrace_spec.rb +44 -44
  52. data/spec/code_hunk_spec.rb +11 -11
  53. data/spec/config/processor_spec.rb +143 -0
  54. data/spec/config/validator_spec.rb +23 -6
  55. data/spec/config_spec.rb +40 -14
  56. data/spec/deploy_notifier_spec.rb +2 -2
  57. data/spec/filter_chain_spec.rb +28 -1
  58. data/spec/filters/dependency_filter_spec.rb +1 -1
  59. data/spec/filters/gem_root_filter_spec.rb +9 -9
  60. data/spec/filters/git_last_checkout_filter_spec.rb +21 -4
  61. data/spec/filters/git_repository_filter.rb +1 -1
  62. data/spec/filters/git_revision_filter_spec.rb +10 -10
  63. data/spec/filters/{keys_whitelist_spec.rb → keys_allowlist_spec.rb} +29 -28
  64. data/spec/filters/{keys_blacklist_spec.rb → keys_blocklist_spec.rb} +39 -29
  65. data/spec/filters/root_directory_filter_spec.rb +9 -9
  66. data/spec/filters/sql_filter_spec.rb +58 -60
  67. data/spec/filters/system_exit_filter_spec.rb +1 -1
  68. data/spec/filters/thread_filter_spec.rb +32 -30
  69. data/spec/fixtures/project_root/code.rb +9 -9
  70. data/spec/loggable_spec.rb +17 -0
  71. data/spec/monotonic_time_spec.rb +11 -0
  72. data/spec/notice_notifier/options_spec.rb +17 -17
  73. data/spec/notice_notifier_spec.rb +20 -20
  74. data/spec/notice_spec.rb +6 -6
  75. data/spec/performance_breakdown_spec.rb +0 -1
  76. data/spec/performance_notifier_spec.rb +220 -73
  77. data/spec/query_spec.rb +1 -1
  78. data/spec/queue_spec.rb +18 -0
  79. data/spec/remote_settings/callback_spec.rb +143 -0
  80. data/spec/remote_settings/settings_data_spec.rb +348 -0
  81. data/spec/remote_settings_spec.rb +187 -0
  82. data/spec/request_spec.rb +1 -3
  83. data/spec/response_spec.rb +8 -8
  84. data/spec/spec_helper.rb +6 -6
  85. data/spec/stat_spec.rb +2 -12
  86. data/spec/sync_sender_spec.rb +14 -12
  87. data/spec/tdigest_spec.rb +7 -7
  88. data/spec/thread_pool_spec.rb +39 -10
  89. data/spec/timed_trace_spec.rb +1 -1
  90. data/spec/truncator_spec.rb +12 -12
  91. metadata +32 -14
@@ -0,0 +1,187 @@
1
+ RSpec.describe Airbrake::RemoteSettings do
2
+ let(:project_id) { 123 }
3
+ let(:host) { 'https://v1-production-notifier-configs.s3.amazonaws.com' }
4
+
5
+ let(:endpoint) do
6
+ "#{host}/2020-06-18/config/#{project_id}/config.json"
7
+ end
8
+
9
+ let(:body) do
10
+ {
11
+ 'poll_sec' => 1,
12
+ 'settings' => [
13
+ {
14
+ 'name' => 'apm',
15
+ 'enabled' => false,
16
+ },
17
+ {
18
+ 'name' => 'errors',
19
+ 'enabled' => true,
20
+ },
21
+ ],
22
+ }
23
+ end
24
+
25
+ let!(:stub) do
26
+ stub_request(:get, Regexp.new(endpoint))
27
+ .to_return(status: 200, body: body.to_json)
28
+ end
29
+
30
+ describe ".poll" do
31
+ describe "config loading" do
32
+ let(:settings_data) { described_class::SettingsData.new(project_id, body) }
33
+
34
+ before do
35
+ allow(described_class::SettingsData).to receive(:new).and_return(settings_data)
36
+ end
37
+
38
+ it "yields the config to the block twice" do
39
+ block = proc {}
40
+ expect(block).to receive(:call).twice
41
+
42
+ remote_settings = described_class.poll(project_id, host, &block)
43
+ sleep(0.2)
44
+ remote_settings.stop_polling
45
+
46
+ expect(stub).to have_been_requested.once
47
+ end
48
+ end
49
+
50
+ context "when no errors are raised" do
51
+ it "makes a request to AWS S3" do
52
+ remote_settings = described_class.poll(project_id, host) {}
53
+ sleep(0.1)
54
+ remote_settings.stop_polling
55
+
56
+ expect(stub).to have_been_requested.at_least_once
57
+ end
58
+
59
+ it "sends params about the environment with the request" do
60
+ remote_settings = described_class.poll(project_id, host) {}
61
+ sleep(0.1)
62
+ remote_settings.stop_polling
63
+
64
+ stub_with_query_params = stub.with(
65
+ query: URI.decode_www_form(described_class::QUERY_PARAMS).to_h,
66
+ )
67
+ expect(stub_with_query_params).to have_been_requested.at_least_once
68
+ end
69
+
70
+ it "fetches remote settings" do
71
+ settings = nil
72
+ remote_settings = described_class.poll(project_id, host) do |data|
73
+ settings = data
74
+ end
75
+ sleep(0.1)
76
+ remote_settings.stop_polling
77
+
78
+ expect(settings.error_notifications?).to eq(true)
79
+ expect(settings.performance_stats?).to eq(false)
80
+ expect(settings.interval).to eq(1)
81
+ end
82
+ end
83
+
84
+ context "when an error is raised while making a HTTP request" do
85
+ before do
86
+ allow(Net::HTTP).to receive(:get_response).and_raise(StandardError)
87
+ end
88
+
89
+ it "doesn't fetch remote settings" do
90
+ settings = nil
91
+ remote_settings = described_class.poll(project_id, host) do |data|
92
+ settings = data
93
+ end
94
+ sleep(0.1)
95
+ remote_settings.stop_polling
96
+
97
+ expect(stub).not_to have_been_requested
98
+ expect(settings.interval).to eq(600)
99
+ end
100
+ end
101
+
102
+ context "when an error is raised while parsing returned JSON" do
103
+ before do
104
+ allow(JSON).to receive(:parse).and_raise(JSON::ParserError)
105
+ end
106
+
107
+ it "doesn't update settings data" do
108
+ settings = nil
109
+ remote_settings = described_class.poll(project_id, host) do |data|
110
+ settings = data
111
+ end
112
+ sleep(0.1)
113
+ remote_settings.stop_polling
114
+
115
+ expect(stub).to have_been_requested.once
116
+ expect(settings.interval).to eq(600)
117
+ end
118
+ end
119
+
120
+ context "when API returns a non-200 response" do
121
+ let!(:stub) do
122
+ stub_request(:get, Regexp.new(endpoint))
123
+ .to_return(status: 201, body: body.to_json)
124
+ end
125
+
126
+ it "doesn't update settings data" do
127
+ settings = nil
128
+ remote_settings = described_class.poll(project_id, host) do |data|
129
+ settings = data
130
+ end
131
+ sleep(0.1)
132
+ remote_settings.stop_polling
133
+
134
+ expect(stub).to have_been_requested.once
135
+ expect(settings.interval).to eq(600)
136
+ end
137
+
138
+ it "logs error" do
139
+ expect(Airbrake::Loggable.instance).to receive(:error).with(body.to_json)
140
+
141
+ remote_settings = described_class.poll(project_id, host) {}
142
+ sleep(0.1)
143
+ remote_settings.stop_polling
144
+ end
145
+ end
146
+
147
+ context "when API returns a 200 response" do
148
+ let!(:stub) do
149
+ stub_request(:get, Regexp.new(endpoint))
150
+ .to_return(status: 200, body: body.to_json)
151
+ end
152
+
153
+ it "doesn't log errors" do
154
+ expect(Airbrake::Loggable.instance).not_to receive(:error)
155
+
156
+ remote_settings = described_class.poll(project_id, host) {}
157
+ sleep(0.1)
158
+ remote_settings.stop_polling
159
+ end
160
+ end
161
+
162
+ context "when a config route is specified in the returned data" do
163
+ let(:new_config_route) do
164
+ '213/config/111/config.json'
165
+ end
166
+
167
+ let(:body) do
168
+ { 'config_route' => new_config_route, 'poll_sec' => 0.1 }
169
+ end
170
+
171
+ let!(:new_stub) do
172
+ stub_request(:get, Regexp.new(new_config_route))
173
+ .to_return(status: 200, body: body.to_json)
174
+ end
175
+
176
+ it "makes the next request to the specified config route" do
177
+ remote_settings = described_class.poll(project_id, host) {}
178
+ sleep(0.2)
179
+
180
+ remote_settings.stop_polling
181
+
182
+ expect(stub).to have_been_requested.once
183
+ expect(new_stub).to have_been_requested.once
184
+ end
185
+ end
186
+ end
187
+ end
@@ -1,9 +1,7 @@
1
1
  RSpec.describe Airbrake::Request do
2
2
  describe "#stash" do
3
3
  subject do
4
- described_class.new(
5
- method: 'GET', route: '/', status_code: 200, start_time: Time.now
6
- )
4
+ described_class.new(method: 'GET', route: '/', status_code: 200)
7
5
  end
8
6
 
9
7
  it { is_expected.to respond_to(:stash) }
@@ -4,7 +4,7 @@ RSpec.describe Airbrake::Response do
4
4
  context "when response code is #{code}" do
5
5
  it "logs response body" do
6
6
  expect(Airbrake::Loggable.instance).to receive(:debug).with(
7
- /Airbrake::Response \(#{code}\): {}/
7
+ /Airbrake::Response \(#{code}\): {}/,
8
8
  )
9
9
  described_class.parse(OpenStruct.new(code: code, body: '{}'))
10
10
  end
@@ -15,10 +15,10 @@ RSpec.describe Airbrake::Response do
15
15
  context "when response code is #{code}" do
16
16
  it "logs response message" do
17
17
  expect(Airbrake::Loggable.instance).to receive(:error).with(
18
- /Airbrake: foo/
18
+ /Airbrake: foo/,
19
19
  )
20
20
  described_class.parse(
21
- OpenStruct.new(code: code, body: '{"message":"foo"}')
21
+ OpenStruct.new(code: code, body: '{"message":"foo"}'),
22
22
  )
23
23
  end
24
24
  end
@@ -29,7 +29,7 @@ RSpec.describe Airbrake::Response do
29
29
 
30
30
  it "logs response message" do
31
31
  expect(Airbrake::Loggable.instance).to receive(:error).with(
32
- /Airbrake: rate limited/
32
+ /Airbrake: rate limited/,
33
33
  )
34
34
  described_class.parse(response)
35
35
  end
@@ -41,7 +41,7 @@ RSpec.describe Airbrake::Response do
41
41
  resp = described_class.parse(response)
42
42
  expect(resp).to include(
43
43
  'error' => '**Airbrake: rate limited',
44
- 'rate_limit_reset' => time
44
+ 'rate_limit_reset' => time,
45
45
  )
46
46
  end
47
47
  end
@@ -51,7 +51,7 @@ RSpec.describe Airbrake::Response do
51
51
 
52
52
  it "logs response body" do
53
53
  expect(Airbrake::Loggable.instance).to receive(:error).with(
54
- /Airbrake: unexpected code \(500\)\. Body: foo/
54
+ /Airbrake: unexpected code \(500\)\. Body: foo/,
55
55
  )
56
56
  described_class.parse(response)
57
57
  end
@@ -73,14 +73,14 @@ RSpec.describe Airbrake::Response do
73
73
 
74
74
  it "logs response body" do
75
75
  expect(Airbrake::Loggable.instance).to receive(:error).with(
76
- /Airbrake: error while parsing body \(.*unexpected token.*\)\. Body: foo/
76
+ /Airbrake: error while parsing body \(.*unexpected token.*\)\. Body: foo/,
77
77
  )
78
78
  described_class.parse(response)
79
79
  end
80
80
 
81
81
  it "returns an error message" do
82
82
  expect(described_class.parse(response)['error']).to match(
83
- /\A#<JSON::ParserError.+>/
83
+ /\A#<JSON::ParserError.+>/,
84
84
  )
85
85
  end
86
86
  end
@@ -33,7 +33,7 @@ class AirbrakeTestError < RuntimeError
33
33
 
34
34
  def initialize(*)
35
35
  super
36
- # rubocop:disable Metrics/LineLength
36
+ # rubocop:disable Layout/LineLength
37
37
  @backtrace = [
38
38
  "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb:23:in `<top (required)>'",
39
39
  "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
@@ -47,9 +47,9 @@ class AirbrakeTestError < RuntimeError
47
47
  "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:88:in `run'",
48
48
  "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:73:in `run'",
49
49
  "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/lib/rspec/core/runner.rb:41:in `invoke'",
50
- "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'"
50
+ "/home/kyrylo/.gem/ruby/2.2.2/gems/rspec-core-3.3.2/exe/rspec:4:in `<main>'",
51
51
  ]
52
- # rubocop:enable Metrics/LineLength
52
+ # rubocop:enable Layout/LineLength
53
53
  end
54
54
 
55
55
  # rubocop:disable Naming/AccessorMethodName
@@ -66,7 +66,7 @@ end
66
66
  class JavaAirbrakeTestError < AirbrakeTestError
67
67
  def initialize(*)
68
68
  super
69
- # rubocop:disable Metrics/LineLength
69
+ # rubocop:disable Layout/LineLength
70
70
  @backtrace = [
71
71
  "org.jruby.java.invokers.InstanceMethodInvoker.call(InstanceMethodInvoker.java:26)",
72
72
  "org.jruby.ir.interpreter.Interpreter.INTERPRET_EVAL(Interpreter.java:126)",
@@ -78,9 +78,9 @@ class JavaAirbrakeTestError < AirbrakeTestError
78
78
  "opt.rubies.jruby_minus_9_dot_0_dot_0_dot_0.bin.irb.RUBY$script(/opt/rubies/jruby-9.0.0.0/bin/irb:13)",
79
79
  "org.jruby.ir.Compiler$1.load(Compiler.java:111)",
80
80
  "org.jruby.Main.run(Main.java:225)",
81
- "org.jruby.Main.main(Main.java:197)"
81
+ "org.jruby.Main.main(Main.java:197)",
82
82
  ]
83
- # rubocop:enable Metrics/LineLength
83
+ # rubocop:enable Layout/LineLength
84
84
  end
85
85
 
86
86
  def is_a?(*)
@@ -5,24 +5,14 @@ RSpec.describe Airbrake::Stat do
5
5
  'count' => 0,
6
6
  'sum' => 0.0,
7
7
  'sumsq' => 0.0,
8
- 'tdigest' => 'AAAAAkA0AAAAAAAAAAAAAA=='
8
+ 'tdigest' => 'AAAAAkA0AAAAAAAAAAAAAA==',
9
9
  )
10
10
  end
11
11
  end
12
12
 
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
13
  describe "#increment_ms" do
23
14
  before { subject.increment_ms(1000) }
24
15
 
25
- its(:count) { is_expected.to eq(1) }
26
16
  its(:sum) { is_expected.to eq(1000) }
27
17
  its(:sumsq) { is_expected.to eq(1000000) }
28
18
 
@@ -34,7 +24,7 @@ RSpec.describe Airbrake::Stat do
34
24
  describe "#inspect" do
35
25
  it "provides custom inspect output" do
36
26
  expect(subject.inspect).to eq(
37
- '#<struct Airbrake::Stat count=0, sum=0.0, sumsq=0.0>'
27
+ '#<struct Airbrake::Stat count=0, sum=0.0, sumsq=0.0>',
38
28
  )
39
29
  end
40
30
  end
@@ -1,7 +1,7 @@
1
1
  RSpec.describe Airbrake::SyncSender do
2
2
  before do
3
3
  Airbrake::Config.instance = Airbrake::Config.new(
4
- project_id: 1, project_key: 'banana'
4
+ project_id: 1, project_key: 'banana',
5
5
  )
6
6
  end
7
7
 
@@ -17,8 +17,8 @@ RSpec.describe Airbrake::SyncSender do
17
17
  subject.send({}, promise)
18
18
  expect(
19
19
  a_request(:post, endpoint).with(
20
- headers: { 'Content-Type' => 'application/json' }
21
- )
20
+ headers: { 'Content-Type' => 'application/json' },
21
+ ),
22
22
  ).to have_been_made.once
23
23
  end
24
24
 
@@ -27,9 +27,11 @@ RSpec.describe Airbrake::SyncSender do
27
27
  expect(
28
28
  a_request(:post, endpoint).with(
29
29
  headers: {
30
- 'User-Agent' => %r{airbrake-ruby/\d+\.\d+\.\d+ Ruby/\d+\.\d+\.\d+}
31
- }
32
- )
30
+ 'User-Agent' => %r{
31
+ airbrake-ruby/\d+\.\d+\.\d+(\.rc\.\d+)?\sRuby/\d+\.\d+\.\d+
32
+ }x,
33
+ },
34
+ ),
33
35
  ).to have_been_made.once
34
36
  end
35
37
 
@@ -37,8 +39,8 @@ RSpec.describe Airbrake::SyncSender do
37
39
  subject.send({}, promise)
38
40
  expect(
39
41
  a_request(:post, endpoint).with(
40
- headers: { 'Authorization' => 'Bearer banana' }
41
- )
42
+ headers: { 'Authorization' => 'Bearer banana' },
43
+ ),
42
44
  ).to have_been_made.once
43
45
  end
44
46
 
@@ -47,7 +49,7 @@ RSpec.describe Airbrake::SyncSender do
47
49
  allow(subject).to receive(:build_https).and_return(https)
48
50
  allow(https).to receive(:request).and_raise(StandardError.new('foo'))
49
51
  expect(Airbrake::Loggable.instance).to receive(:error).with(
50
- /HTTP error: foo/
52
+ /HTTP error: foo/,
51
53
  )
52
54
  expect(subject.send({}, promise)).to be_an(Airbrake::Promise)
53
55
  expect(promise.value).to eq('error' => '**Airbrake: HTTP error: foo')
@@ -69,10 +71,10 @@ RSpec.describe Airbrake::SyncSender do
69
71
  notice = Airbrake::Notice.new(ex)
70
72
 
71
73
  expect(Airbrake::Loggable.instance).to receive(:error).with(
72
- /data was not sent/
74
+ /data was not sent/,
73
75
  )
74
76
  expect(Airbrake::Loggable.instance).to receive(:error).with(
75
- /truncation failed/
77
+ /truncation failed/,
76
78
  )
77
79
  expect(subject.send(notice, promise)).to be_an(Airbrake::Promise)
78
80
  expect(promise.value)
@@ -87,7 +89,7 @@ RSpec.describe Airbrake::SyncSender do
87
89
  stub_request(:post, endpoint).to_return(
88
90
  status: 429,
89
91
  body: '{"message":"IP is rate limited"}',
90
- headers: { 'X-RateLimit-Delay' => '1' }
92
+ headers: { 'X-RateLimit-Delay' => '1' },
91
93
  )
92
94
  end
93
95
 
@@ -29,7 +29,7 @@ RSpec.describe Airbrake::TDigest do
29
29
  new_tdigest = described_class.from_bytes(bytes)
30
30
  # Expect some rounding error due to compression
31
31
  expect(new_tdigest.percentile(0.9).round(5)).to eq(
32
- subject.percentile(0.9).round(5)
32
+ subject.percentile(0.9).round(5),
33
33
  )
34
34
  expect(new_tdigest.as_small_bytes).to eq(bytes)
35
35
  end
@@ -86,7 +86,7 @@ RSpec.describe Airbrake::TDigest do
86
86
  maxerr = [maxerr, (i - q).abs].max
87
87
  end
88
88
 
89
- expect(maxerr).to be < 0.01
89
+ expect(maxerr).to be < 0.02
90
90
  end
91
91
  end
92
92
  end
@@ -126,8 +126,8 @@ RSpec.describe Airbrake::TDigest do
126
126
  113270270.27027026,
127
127
  154459459.45945945,
128
128
  123829787.23404256,
129
- 103191489.36170213
130
- ]
129
+ 103191489.36170213,
130
+ ],
131
131
  )
132
132
  end
133
133
 
@@ -166,13 +166,13 @@ RSpec.describe Airbrake::TDigest do
166
166
  it "has the parameters of the left argument (the calling tdigest)" do
167
167
  new_tdigest = subject + @other
168
168
  expect(new_tdigest.instance_variable_get(:@delta)).to eq(
169
- subject.instance_variable_get(:@delta)
169
+ subject.instance_variable_get(:@delta),
170
170
  )
171
171
  expect(new_tdigest.instance_variable_get(:@k)).to eq(
172
- subject.instance_variable_get(:@k)
172
+ subject.instance_variable_get(:@k),
173
173
  )
174
174
  expect(new_tdigest.instance_variable_get(:@cx)).to eq(
175
- subject.instance_variable_get(:@cx)
175
+ subject.instance_variable_get(:@cx),
176
176
  )
177
177
  end
178
178