airbrake-ruby 5.2.0 → 5.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
data/spec/promise_spec.rb CHANGED
@@ -1,15 +1,17 @@
1
1
  RSpec.describe Airbrake::Promise do
2
+ subject(:promise) { described_class.new }
3
+
2
4
  describe ".then" do
3
5
  let(:resolved_with) { [] }
4
6
  let(:rejected_with) { [] }
5
7
 
6
8
  context "when it is not resolved" do
7
9
  it "returns self" do
8
- expect(subject.then {}).to eq(subject)
10
+ expect(promise.then { anything }).to eq(promise)
9
11
  end
10
12
 
11
13
  it "doesn't call the resolve callbacks yet" do
12
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
14
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
13
15
  expect(resolved_with).to be_empty
14
16
  end
15
17
  end
@@ -17,12 +19,12 @@ RSpec.describe Airbrake::Promise do
17
19
  context "when it is resolved" do
18
20
  shared_examples "then specs" do
19
21
  it "returns self" do
20
- expect(subject.then {}).to eq(subject)
22
+ expect(promise.then { anything }).to eq(promise)
21
23
  end
22
24
 
23
25
  it "yields the resolved value" do
24
26
  yielded = nil
25
- subject.then { |value| yielded = value }
27
+ promise.then { |value| yielded = value }
26
28
  expect(yielded).to eq('id' => '123')
27
29
  end
28
30
 
@@ -37,29 +39,29 @@ RSpec.describe Airbrake::Promise do
37
39
 
38
40
  context "and there are some resolve and reject callbacks in place" do
39
41
  before do
40
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
41
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
42
- subject.resolve('id' => '123')
42
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
43
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
44
+ promise.resolve('id' => '123')
43
45
  end
44
46
 
45
47
  include_examples "then specs"
46
48
 
47
49
  it "registers the resolve callbacks" do
48
- subject.resolve('id' => '456')
50
+ promise.resolve('id' => '456')
49
51
  expect(resolved_with).to match_array([1, 2, 1, 2])
50
52
  end
51
53
  end
52
54
 
53
55
  context "and additional then callbacks are added" do
54
56
  before do
55
- subject.resolve('id' => '123')
56
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
57
+ promise.resolve('id' => '123')
58
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
57
59
  end
58
60
 
59
61
  include_examples "then specs"
60
62
 
61
63
  it "doesn't register new resolve callbacks" do
62
- subject.resolve('id' => '456')
64
+ promise.resolve('id' => '456')
63
65
  expect(resolved_with).to match_array([1, 2])
64
66
  end
65
67
  end
@@ -72,11 +74,11 @@ RSpec.describe Airbrake::Promise do
72
74
 
73
75
  context "when it is not rejected" do
74
76
  it "returns self" do
75
- expect(subject.then {}).to eq(subject)
77
+ expect(promise.then { anything }).to eq(promise)
76
78
  end
77
79
 
78
80
  it "doesn't call the reject callbacks yet" do
79
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
81
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
80
82
  expect(rejected_with).to be_empty
81
83
  end
82
84
  end
@@ -84,12 +86,12 @@ RSpec.describe Airbrake::Promise do
84
86
  context "when it is rejected" do
85
87
  shared_examples "rescue specs" do
86
88
  it "returns self" do
87
- expect(subject.rescue {}).to eq(subject)
89
+ expect(promise.rescue { anything }).to eq(promise)
88
90
  end
89
91
 
90
92
  it "yields the rejected value" do
91
93
  yielded = nil
92
- subject.rescue { |value| yielded = value }
94
+ promise.rescue { |value| yielded = value }
93
95
  expect(yielded).to eq('bingo')
94
96
  end
95
97
 
@@ -104,29 +106,29 @@ RSpec.describe Airbrake::Promise do
104
106
 
105
107
  context "and there are some resolve and reject callbacks in place" do
106
108
  before do
107
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
108
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
109
- subject.reject('bingo')
109
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
110
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
111
+ promise.reject('bingo')
110
112
  end
111
113
 
112
114
  include_examples "rescue specs"
113
115
 
114
116
  it "registers the reject callbacks" do
115
- subject.reject('bingo again')
117
+ promise.reject('bingo again')
116
118
  expect(rejected_with).to match_array([1, 2, 1, 2])
117
119
  end
118
120
  end
119
121
 
120
122
  context "and additional reject callbacks are added" do
121
123
  before do
122
- subject.reject('bingo')
123
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
124
+ promise.reject('bingo')
125
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
124
126
  end
125
127
 
126
128
  include_examples "rescue specs"
127
129
 
128
130
  it "doesn't register new reject callbacks" do
129
- subject.reject('bingo again')
131
+ promise.reject('bingo again')
130
132
  expect(rejected_with).to match_array([1, 2])
131
133
  end
132
134
  end
@@ -135,37 +137,38 @@ RSpec.describe Airbrake::Promise do
135
137
 
136
138
  describe ".resolve" do
137
139
  it "returns self" do
138
- expect(subject.resolve(1)).to eq(subject)
140
+ expect(promise.resolve(1)).to eq(promise)
139
141
  end
140
142
 
141
143
  it "executes callbacks attached with .then" do
142
144
  array = []
143
- subject.then { |notice_id| array << notice_id }.rescue { array << 999 }
145
+ promise.then { |notice_id| array << notice_id }.rescue { array << 999 }
144
146
 
145
147
  expect(array.size).to be_zero
146
- subject.resolve(1)
148
+ promise.resolve(1)
147
149
  expect(array).to match_array([1])
148
150
  end
149
151
  end
150
152
 
151
153
  describe ".reject" do
152
154
  it "returns self" do
153
- expect(subject.reject(1)).to eq(subject)
155
+ expect(promise.reject(1)).to eq(promise)
154
156
  end
155
157
 
156
158
  it "executes callbacks attached with .rescue" do
157
159
  array = []
158
- subject.then { array << 1 }.rescue { |error| array << error }
160
+ promise.then { array << 1 }.rescue { |error| array << error }
159
161
 
160
162
  expect(array.size).to be_zero
161
- subject.reject(999)
163
+ promise.reject(999)
162
164
  expect(array).to match_array([999])
163
165
  end
164
166
  end
165
167
 
166
168
  describe "#rejected?" do
167
169
  context "when it was rejected" do
168
- before { subject.reject(1) }
170
+ before { promise.reject(1) }
171
+
169
172
  it { is_expected.to be_rejected }
170
173
  end
171
174
 
@@ -174,14 +177,16 @@ RSpec.describe Airbrake::Promise do
174
177
  end
175
178
 
176
179
  context "when it was resolved" do
177
- before { subject.resolve }
180
+ before { promise.resolve }
181
+
178
182
  it { is_expected.not_to be_rejected }
179
183
  end
180
184
  end
181
185
 
182
186
  describe "#resolved?" do
183
187
  context "when it was resolved" do
184
- before { subject.resolve }
188
+ before { promise.resolve }
189
+
185
190
  it { is_expected.to be_resolved }
186
191
  end
187
192
 
@@ -190,7 +195,8 @@ RSpec.describe Airbrake::Promise do
190
195
  end
191
196
 
192
197
  context "when it was rejected" do
193
- before { subject.reject(1) }
198
+ before { promise.reject(1) }
199
+
194
200
  it { is_expected.not_to be_resolved }
195
201
  end
196
202
  end
@@ -22,10 +22,13 @@ RSpec.describe Airbrake::RemoteSettings::Callback do
22
22
  end
23
23
 
24
24
  it "logs given data" do
25
- expect(logger).to receive(:debug) do |&block|
25
+ allow(logger).to receive(:debug)
26
+
27
+ described_class.new(config).call(data)
28
+
29
+ expect(logger).to have_received(:debug) do |&block|
26
30
  expect(block.call).to match(/applying remote settings/)
27
31
  end
28
- described_class.new(config).call(data)
29
32
  end
30
33
 
31
34
  context "when the config disables error notifications" do
@@ -34,6 +37,7 @@ RSpec.describe Airbrake::RemoteSettings::Callback do
34
37
  allow(data).to receive(:error_notifications?).and_return(true)
35
38
  end
36
39
 
40
+ # rubocop:disable RSpec/MultipleExpectations
37
41
  it "keeps the option disabled forever" do
38
42
  callback = described_class.new(config)
39
43
 
@@ -46,22 +50,29 @@ RSpec.describe Airbrake::RemoteSettings::Callback do
46
50
  callback.call(data)
47
51
  expect(config.error_notifications).to eq(false)
48
52
  end
53
+ # rubocop:enable RSpec/MultipleExpectations
49
54
  end
50
55
 
51
56
  context "when the config enables error notifications" do
52
57
  before { config.error_notifications = true }
53
58
 
59
+ # rubocop:disable RSpec/MultipleExpectations
54
60
  it "can disable and enable error notifications" do
55
- expect(data).to receive(:error_notifications?).and_return(false)
56
-
57
61
  callback = described_class.new(config)
62
+
63
+ allow(data).to receive(:error_notifications?).and_return(false)
64
+
58
65
  callback.call(data)
59
66
  expect(config.error_notifications).to eq(false)
60
67
 
61
- expect(data).to receive(:error_notifications?).and_return(true)
68
+ allow(data).to receive(:error_notifications?).and_return(true)
69
+
62
70
  callback.call(data)
63
71
  expect(config.error_notifications).to eq(true)
72
+
73
+ expect(data).to have_received(:error_notifications?).twice
64
74
  end
75
+ # rubocop:enable RSpec/MultipleExpectations
65
76
  end
66
77
 
67
78
  context "when the config disables performance_stats" do
@@ -70,6 +81,7 @@ RSpec.describe Airbrake::RemoteSettings::Callback do
70
81
  allow(data).to receive(:performance_stats?).and_return(true)
71
82
  end
72
83
 
84
+ # rubocop:disable RSpec/MultipleExpectations
73
85
  it "keeps the option disabled forever" do
74
86
  callback = described_class.new(config)
75
87
 
@@ -82,22 +94,29 @@ RSpec.describe Airbrake::RemoteSettings::Callback do
82
94
  callback.call(data)
83
95
  expect(config.performance_stats).to eq(false)
84
96
  end
97
+ # rubocop:enable RSpec/MultipleExpectations
85
98
  end
86
99
 
87
100
  context "when the config enables performance stats" do
88
101
  before { config.performance_stats = true }
89
102
 
103
+ # rubocop:disable RSpec/MultipleExpectations
90
104
  it "can disable and enable performance_stats" do
91
- expect(data).to receive(:performance_stats?).and_return(false)
92
-
93
105
  callback = described_class.new(config)
106
+
107
+ allow(data).to receive(:performance_stats?).and_return(false)
108
+
94
109
  callback.call(data)
95
110
  expect(config.performance_stats).to eq(false)
96
111
 
97
- expect(data).to receive(:performance_stats?).and_return(true)
112
+ allow(data).to receive(:performance_stats?).and_return(true)
113
+
98
114
  callback.call(data)
99
115
  expect(config.performance_stats).to eq(true)
116
+
117
+ expect(data).to have_received(:performance_stats?).twice
100
118
  end
119
+ # rubocop:enable RSpec/MultipleExpectations
101
120
  end
102
121
 
103
122
  context "when error_host returns a value" do
@@ -312,6 +312,8 @@ RSpec.describe Airbrake::RemoteSettings::SettingsData do
312
312
  end
313
313
 
314
314
  describe "#to_h" do
315
+ subject(:settings_data) { described_class.new(project_id, data) }
316
+
315
317
  let(:data) do
316
318
  {
317
319
  'poll_sec' => 123,
@@ -324,17 +326,15 @@ RSpec.describe Airbrake::RemoteSettings::SettingsData do
324
326
  }
325
327
  end
326
328
 
327
- subject { described_class.new(project_id, data) }
328
-
329
329
  it "returns a hash representation of settings" do
330
330
  expect(described_class.new(project_id, data).to_h).to eq(data)
331
331
  end
332
332
 
333
333
  it "doesn't allow mutation of the original data object" do
334
- hash = subject.to_h
334
+ hash = settings_data.to_h
335
335
  hash['poll_sec'] = 0
336
336
 
337
- expect(subject.to_h).to eq(
337
+ expect(settings_data.to_h).to eq(
338
338
  'poll_sec' => 123,
339
339
  'settings' => [
340
340
  {
@@ -37,19 +37,20 @@ RSpec.describe Airbrake::RemoteSettings do
37
37
 
38
38
  it "yields the config to the block twice" do
39
39
  block = proc {}
40
- expect(block).to receive(:call).twice
40
+ allow(block).to receive(:call)
41
41
 
42
42
  remote_settings = described_class.poll(project_id, host, &block)
43
43
  sleep(0.2)
44
44
  remote_settings.stop_polling
45
45
 
46
46
  expect(stub).to have_been_requested.once
47
+ expect(block).to have_received(:call).twice
47
48
  end
48
49
  end
49
50
 
50
51
  context "when no errors are raised" do
51
52
  it "makes a request to AWS S3" do
52
- remote_settings = described_class.poll(project_id, host) {}
53
+ remote_settings = described_class.poll(project_id, host) { anything }
53
54
  sleep(0.1)
54
55
  remote_settings.stop_polling
55
56
 
@@ -57,7 +58,7 @@ RSpec.describe Airbrake::RemoteSettings do
57
58
  end
58
59
 
59
60
  it "sends params about the environment with the request" do
60
- remote_settings = described_class.poll(project_id, host) {}
61
+ remote_settings = described_class.poll(project_id, host) { anything }
61
62
  sleep(0.1)
62
63
  remote_settings.stop_polling
63
64
 
@@ -67,6 +68,7 @@ RSpec.describe Airbrake::RemoteSettings do
67
68
  expect(stub_with_query_params).to have_been_requested.at_least_once
68
69
  end
69
70
 
71
+ # rubocop:disable RSpec/MultipleExpectations
70
72
  it "fetches remote settings" do
71
73
  settings = nil
72
74
  remote_settings = described_class.poll(project_id, host) do |data|
@@ -79,6 +81,7 @@ RSpec.describe Airbrake::RemoteSettings do
79
81
  expect(settings.performance_stats?).to eq(false)
80
82
  expect(settings.interval).to eq(1)
81
83
  end
84
+ # rubocop:enable RSpec/MultipleExpectations
82
85
  end
83
86
 
84
87
  context "when an error is raised while making a HTTP request" do
@@ -136,11 +139,13 @@ RSpec.describe Airbrake::RemoteSettings do
136
139
  end
137
140
 
138
141
  it "logs error" do
139
- expect(Airbrake::Loggable.instance).to receive(:error).with(body.to_json)
142
+ allow(Airbrake::Loggable.instance).to receive(:error)
140
143
 
141
- remote_settings = described_class.poll(project_id, host) {}
144
+ remote_settings = described_class.poll(project_id, host) { anything }
142
145
  sleep(0.1)
143
146
  remote_settings.stop_polling
147
+
148
+ expect(Airbrake::Loggable.instance).to have_received(:error).with(body.to_json)
144
149
  end
145
150
  end
146
151
 
@@ -151,14 +156,18 @@ RSpec.describe Airbrake::RemoteSettings do
151
156
  end
152
157
 
153
158
  it "doesn't log errors" do
154
- expect(Airbrake::Loggable.instance).not_to receive(:error)
159
+ allow(Airbrake::Loggable.instance).to receive(:error)
155
160
 
156
- remote_settings = described_class.poll(project_id, host) {}
161
+ remote_settings = described_class.poll(project_id, host) { anything }
157
162
  sleep(0.1)
158
163
  remote_settings.stop_polling
164
+
165
+ expect(Airbrake::Loggable.instance).not_to have_received(:error)
166
+ expect(stub).to have_been_requested.once
159
167
  end
160
168
  end
161
169
 
170
+ # rubocop:disable RSpec/MultipleMemoizedHelpers
162
171
  context "when a config route is specified in the returned data" do
163
172
  let(:new_config_route) do
164
173
  '213/config/111/config.json'
@@ -174,7 +183,7 @@ RSpec.describe Airbrake::RemoteSettings do
174
183
  end
175
184
 
176
185
  it "makes the next request to the specified config route" do
177
- remote_settings = described_class.poll(project_id, host) {}
186
+ remote_settings = described_class.poll(project_id, host) { anything }
178
187
  sleep(0.2)
179
188
 
180
189
  remote_settings.stop_polling
@@ -183,5 +192,6 @@ RSpec.describe Airbrake::RemoteSettings do
183
192
  expect(new_stub).to have_been_requested.once
184
193
  end
185
194
  end
195
+ # rubocop:enable RSpec/MultipleMemoizedHelpers
186
196
  end
187
197
  end
@@ -2,24 +2,34 @@ RSpec.describe Airbrake::Response do
2
2
  describe ".parse" do
3
3
  [200, 201, 204].each do |code|
4
4
  context "when response code is #{code}" do
5
+ before do
6
+ allow(Airbrake::Loggable.instance).to receive(:debug)
7
+ end
8
+
5
9
  it "logs response body" do
6
- expect(Airbrake::Loggable.instance).to receive(:debug).with(
10
+ described_class.parse(OpenStruct.new(code: code, body: '{}'))
11
+
12
+ expect(Airbrake::Loggable.instance).to have_received(:debug).with(
7
13
  /Airbrake::Response \(#{code}\): {}/,
8
14
  )
9
- described_class.parse(OpenStruct.new(code: code, body: '{}'))
10
15
  end
11
16
  end
12
17
  end
13
18
 
14
19
  [400, 401, 403, 420].each do |code|
15
20
  context "when response code is #{code}" do
21
+ before do
22
+ allow(Airbrake::Loggable.instance).to receive(:error)
23
+ end
24
+
16
25
  it "logs response message" do
17
- expect(Airbrake::Loggable.instance).to receive(:error).with(
18
- /Airbrake: foo/,
19
- )
20
26
  described_class.parse(
21
27
  OpenStruct.new(code: code, body: '{"message":"foo"}'),
22
28
  )
29
+
30
+ expect(Airbrake::Loggable.instance).to have_received(:error).with(
31
+ /Airbrake: foo/,
32
+ )
23
33
  end
24
34
  end
25
35
  end
@@ -27,11 +37,15 @@ RSpec.describe Airbrake::Response do
27
37
  context "when response code is 429" do
28
38
  let(:response) { OpenStruct.new(code: 429, body: '{"message":"rate limited"}') }
29
39
 
40
+ before do
41
+ allow(Airbrake::Loggable.instance).to receive(:error)
42
+ end
43
+
30
44
  it "logs response message" do
31
- expect(Airbrake::Loggable.instance).to receive(:error).with(
45
+ described_class.parse(response)
46
+ expect(Airbrake::Loggable.instance).to have_received(:error).with(
32
47
  /Airbrake: rate limited/,
33
48
  )
34
- described_class.parse(response)
35
49
  end
36
50
 
37
51
  it "returns an error response" do
@@ -49,11 +63,15 @@ RSpec.describe Airbrake::Response do
49
63
  context "when response code is unhandled" do
50
64
  let(:response) { OpenStruct.new(code: 500, body: 'foo') }
51
65
 
66
+ before do
67
+ allow(Airbrake::Loggable.instance).to receive(:error)
68
+ end
69
+
52
70
  it "logs response body" do
53
- expect(Airbrake::Loggable.instance).to receive(:error).with(
71
+ described_class.parse(response)
72
+ expect(Airbrake::Loggable.instance).to have_received(:error).with(
54
73
  /Airbrake: unexpected code \(500\)\. Body: foo/,
55
74
  )
56
- described_class.parse(response)
57
75
  end
58
76
 
59
77
  it "returns an error response" do
@@ -64,18 +82,22 @@ RSpec.describe Airbrake::Response do
64
82
  it "truncates body" do
65
83
  response.body *= 1000
66
84
  resp = described_class.parse(response)
67
- expect(resp).to eq('error' => ('foo' * 33) + 'fo...')
85
+ expect(resp).to eq('error' => "#{'foo' * 33}fo...")
68
86
  end
69
87
  end
70
88
 
71
89
  context "when response body can't be parsed as JSON" do
72
90
  let(:response) { OpenStruct.new(code: 201, body: 'foo') }
73
91
 
92
+ before do
93
+ allow(Airbrake::Loggable.instance).to receive(:error)
94
+ end
95
+
74
96
  it "logs response body" do
75
- expect(Airbrake::Loggable.instance).to receive(:error).with(
97
+ described_class.parse(response)
98
+ expect(Airbrake::Loggable.instance).to have_received(:error).with(
76
99
  /Airbrake: error while parsing body \(.*unexpected token.*\)\. Body: foo/,
77
100
  )
78
- described_class.parse(response)
79
101
  end
80
102
 
81
103
  it "returns an error message" do