airbrake-ruby 4.8.0 → 5.2.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 (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