airbrake-ruby 4.13.3-java → 5.0.0-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.
- checksums.yaml +4 -4
- data/lib/airbrake-ruby.rb +23 -32
- data/lib/airbrake-ruby/async_sender.rb +1 -1
- data/lib/airbrake-ruby/backtrace.rb +6 -5
- data/lib/airbrake-ruby/config.rb +37 -13
- data/lib/airbrake-ruby/config/processor.rb +84 -0
- data/lib/airbrake-ruby/config/validator.rb +6 -0
- data/lib/airbrake-ruby/file_cache.rb +1 -1
- data/lib/airbrake-ruby/filter_chain.rb +16 -1
- data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +3 -3
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +3 -0
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +2 -0
- data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
- data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
- data/lib/airbrake-ruby/filters/keys_filter.rb +26 -18
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +4 -4
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +2 -0
- data/lib/airbrake-ruby/ignorable.rb +1 -0
- data/lib/airbrake-ruby/notice.rb +1 -8
- data/lib/airbrake-ruby/notice_notifier.rb +7 -0
- data/lib/airbrake-ruby/performance_breakdown.rb +1 -6
- data/lib/airbrake-ruby/performance_notifier.rb +2 -15
- data/lib/airbrake-ruby/promise.rb +1 -0
- data/lib/airbrake-ruby/query.rb +1 -6
- data/lib/airbrake-ruby/queue.rb +1 -8
- data/lib/airbrake-ruby/remote_settings.rb +145 -0
- data/lib/airbrake-ruby/remote_settings/settings_data.rb +120 -0
- data/lib/airbrake-ruby/request.rb +1 -8
- data/lib/airbrake-ruby/stat.rb +1 -12
- data/lib/airbrake-ruby/sync_sender.rb +3 -2
- data/lib/airbrake-ruby/tdigest.rb +2 -0
- data/lib/airbrake-ruby/thread_pool.rb +2 -0
- data/lib/airbrake-ruby/truncator.rb +8 -2
- data/lib/airbrake-ruby/version.rb +11 -1
- data/spec/airbrake_spec.rb +71 -36
- data/spec/backtrace_spec.rb +26 -26
- data/spec/code_hunk_spec.rb +2 -2
- data/spec/config/processor_spec.rb +209 -0
- data/spec/config/validator_spec.rb +18 -1
- data/spec/config_spec.rb +13 -6
- data/spec/filter_chain_spec.rb +27 -0
- data/spec/filters/gem_root_filter_spec.rb +4 -4
- data/spec/filters/git_last_checkout_filter_spec.rb +20 -3
- data/spec/filters/{keys_whitelist_spec.rb → keys_allowlist_spec.rb} +11 -10
- data/spec/filters/{keys_blacklist_spec.rb → keys_blocklist_spec.rb} +20 -10
- data/spec/filters/root_directory_filter_spec.rb +4 -4
- data/spec/filters/sql_filter_spec.rb +5 -5
- data/spec/notice_notifier/options_spec.rb +6 -6
- data/spec/notice_notifier_spec.rb +2 -2
- data/spec/notice_spec.rb +1 -1
- data/spec/performance_breakdown_spec.rb +0 -12
- data/spec/performance_notifier_spec.rb +2 -27
- data/spec/query_spec.rb +1 -11
- data/spec/queue_spec.rb +1 -13
- data/spec/remote_settings/settings_data_spec.rb +365 -0
- data/spec/remote_settings_spec.rb +230 -0
- data/spec/request_spec.rb +1 -13
- data/spec/spec_helper.rb +4 -4
- data/spec/stat_spec.rb +0 -9
- data/spec/sync_sender_spec.rb +3 -1
- data/spec/thread_pool_spec.rb +25 -5
- metadata +22 -14
data/spec/queue_spec.rb
CHANGED
@@ -9,21 +9,9 @@ RSpec.describe Airbrake::Queue do
|
|
9
9
|
it { is_expected.to respond_to(:stash) }
|
10
10
|
end
|
11
11
|
|
12
|
-
describe "#end_time" do
|
13
|
-
it "is always equal to start_time + 1 second by default" do
|
14
|
-
time = Time.now
|
15
|
-
queue = described_class.new(
|
16
|
-
queue: 'bananas', error_count: 0, start_time: time,
|
17
|
-
)
|
18
|
-
expect(queue.end_time).to eq(time + 1)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
12
|
describe "#route" do
|
23
13
|
it "always returns an empty route" do
|
24
|
-
queue = described_class.new(
|
25
|
-
queue: 'a', error_count: 0, start_time: Time.now,
|
26
|
-
)
|
14
|
+
queue = described_class.new(queue: 'a', error_count: 0)
|
27
15
|
expect(queue.route).to be_empty
|
28
16
|
end
|
29
17
|
end
|
@@ -0,0 +1,365 @@
|
|
1
|
+
RSpec.describe Airbrake::RemoteSettings::SettingsData do
|
2
|
+
let(:project_id) { 123 }
|
3
|
+
|
4
|
+
describe "#merge!" do
|
5
|
+
it "returns self" do
|
6
|
+
settings_data = described_class.new(project_id, {})
|
7
|
+
expect(settings_data.merge!({})).to eql(settings_data)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "merges the given hash with the data" do
|
11
|
+
settings_data = described_class.new(project_id, {})
|
12
|
+
settings_data.merge!('poll_sec' => 123, 'config_route' => 'abc')
|
13
|
+
|
14
|
+
expect(settings_data.interval).to eq(123)
|
15
|
+
expect(settings_data.config_route(''))
|
16
|
+
.to eq('abc/2020-06-18/config/123/config.json')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#interval" do
|
21
|
+
context "when given data has zero interval" do
|
22
|
+
let(:data) do
|
23
|
+
{ 'poll_sec' => 0 }
|
24
|
+
end
|
25
|
+
|
26
|
+
it "returns the default interval" do
|
27
|
+
expect(described_class.new(project_id, data).interval).to eq(600)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when given data has negative interval" do
|
32
|
+
let(:data) do
|
33
|
+
{ 'poll_sec' => -1 }
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns the default interval" do
|
37
|
+
expect(described_class.new(project_id, data).interval).to eq(600)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when given data has nil interval" do
|
42
|
+
let(:data) do
|
43
|
+
{ 'poll_sec' => nil }
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns the default interval" do
|
47
|
+
expect(described_class.new(project_id, data).interval).to eq(600)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when given data has a positive interval" do
|
52
|
+
let(:data) do
|
53
|
+
{ 'poll_sec' => 123 }
|
54
|
+
end
|
55
|
+
|
56
|
+
it "returns the interval from data" do
|
57
|
+
expect(described_class.new(project_id, data).interval).to eq(123)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#config_route" do
|
63
|
+
let(:host) { 'https://v1-production-notifier-configs.s3.amazonaws.com' }
|
64
|
+
|
65
|
+
context "when given a remote host through the remote config" do
|
66
|
+
context "and when the remote host ends with a slash" do
|
67
|
+
let(:data) do
|
68
|
+
{ 'config_route' => 'http://example.com/' }
|
69
|
+
end
|
70
|
+
|
71
|
+
it "returns the route with the host" do
|
72
|
+
expect(described_class.new(project_id, data).config_route(host)).to eq(
|
73
|
+
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "and when the remote host doesn't with a slash" do
|
79
|
+
let(:data) do
|
80
|
+
{ 'config_route' => 'http://example.com' }
|
81
|
+
end
|
82
|
+
|
83
|
+
it "returns the route with the host" do
|
84
|
+
expect(described_class.new(project_id, data).config_route(host)).to eq(
|
85
|
+
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when given a remote host through local configuration" do
|
92
|
+
context "and when the remote host ends with a slash" do
|
93
|
+
let(:host) { 'http://example.com/' }
|
94
|
+
|
95
|
+
it "returns the route with the host" do
|
96
|
+
expect(described_class.new(project_id, {}).config_route(host)).to eq(
|
97
|
+
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "and when the remote host doesn't with a slash" do
|
103
|
+
let(:host) { 'http://example.com' }
|
104
|
+
|
105
|
+
it "returns the route with the host" do
|
106
|
+
expect(described_class.new(project_id, {}).config_route(host)).to eq(
|
107
|
+
"http://example.com/2020-06-18/config/#{project_id}/config.json",
|
108
|
+
)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "when the given remote host in the remote config is nil" do
|
114
|
+
let(:data) do
|
115
|
+
{ 'config_route' => nil }
|
116
|
+
end
|
117
|
+
|
118
|
+
it "returns the route with the given host instead" do
|
119
|
+
expect(described_class.new(project_id, data).config_route(host)).to eq(
|
120
|
+
'https://v1-production-notifier-configs.s3.amazonaws.com/' \
|
121
|
+
"2020-06-18/config/#{project_id}/config.json",
|
122
|
+
)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "#error_notifications?" do
|
128
|
+
context "when the 'errors' setting is present" do
|
129
|
+
context "and when it is enabled" do
|
130
|
+
let(:data) do
|
131
|
+
{
|
132
|
+
'settings' => [
|
133
|
+
{
|
134
|
+
'name' => 'errors',
|
135
|
+
'enabled' => true,
|
136
|
+
},
|
137
|
+
],
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
it "returns true" do
|
142
|
+
expect(described_class.new(project_id, data).error_notifications?)
|
143
|
+
.to eq(true)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context "and when it is disabled" do
|
148
|
+
let(:data) do
|
149
|
+
{
|
150
|
+
'settings' => [
|
151
|
+
{
|
152
|
+
'name' => 'errors',
|
153
|
+
'enabled' => false,
|
154
|
+
},
|
155
|
+
],
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
it "returns false" do
|
160
|
+
expect(described_class.new(project_id, data).error_notifications?)
|
161
|
+
.to eq(false)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context "when the 'errors' setting is missing" do
|
167
|
+
let(:data) do
|
168
|
+
{ 'settings' => [] }
|
169
|
+
end
|
170
|
+
|
171
|
+
it "returns true" do
|
172
|
+
expect(described_class.new(project_id, data).error_notifications?)
|
173
|
+
.to eq(true)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "#performance_stats?" do
|
179
|
+
context "when the 'apm' setting is present" do
|
180
|
+
context "and when it is enabled" do
|
181
|
+
let(:data) do
|
182
|
+
{
|
183
|
+
'settings' => [
|
184
|
+
{
|
185
|
+
'name' => 'apm',
|
186
|
+
'enabled' => true,
|
187
|
+
},
|
188
|
+
],
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
it "returns true" do
|
193
|
+
expect(described_class.new(project_id, data).performance_stats?)
|
194
|
+
.to eq(true)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context "and when it is disabled" do
|
199
|
+
let(:data) do
|
200
|
+
{
|
201
|
+
'settings' => [
|
202
|
+
{
|
203
|
+
'name' => 'apm',
|
204
|
+
'enabled' => false,
|
205
|
+
},
|
206
|
+
],
|
207
|
+
}
|
208
|
+
end
|
209
|
+
|
210
|
+
it "returns false" do
|
211
|
+
expect(described_class.new(project_id, data).performance_stats?)
|
212
|
+
.to eq(false)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context "when the 'apm' setting is missing" do
|
218
|
+
let(:data) do
|
219
|
+
{ 'settings' => [] }
|
220
|
+
end
|
221
|
+
|
222
|
+
it "returns true" do
|
223
|
+
expect(described_class.new(project_id, data).performance_stats?)
|
224
|
+
.to eq(true)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe "#error_host" do
|
230
|
+
context "when the 'errors' setting is present" do
|
231
|
+
context "and when 'endpoint' is specified" do
|
232
|
+
let(:endpoint) { 'https://api.example.com/' }
|
233
|
+
|
234
|
+
let(:data) do
|
235
|
+
{
|
236
|
+
'settings' => [
|
237
|
+
{
|
238
|
+
'name' => 'errors',
|
239
|
+
'enabled' => true,
|
240
|
+
'endpoint' => endpoint,
|
241
|
+
},
|
242
|
+
],
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
it "returns the endpoint" do
|
247
|
+
expect(described_class.new(project_id, data).error_host).to eq(endpoint)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context "and when an endpoint is NOT specified" do
|
252
|
+
let(:data) do
|
253
|
+
{
|
254
|
+
'settings' => [
|
255
|
+
{
|
256
|
+
'name' => 'errors',
|
257
|
+
'enabled' => true,
|
258
|
+
},
|
259
|
+
],
|
260
|
+
}
|
261
|
+
end
|
262
|
+
|
263
|
+
it "returns nil" do
|
264
|
+
expect(described_class.new(project_id, data).error_host).to be_nil
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
context "when the 'errors' setting is missing" do
|
270
|
+
let(:data) do
|
271
|
+
{ 'settings' => [] }
|
272
|
+
end
|
273
|
+
|
274
|
+
it "returns nil" do
|
275
|
+
expect(described_class.new(project_id, data).error_host).to be_nil
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
describe "#apm_host" do
|
281
|
+
context "when the 'apm' setting is present" do
|
282
|
+
context "and when 'endpoint' is specified" do
|
283
|
+
let(:endpoint) { 'https://api.example.com/' }
|
284
|
+
|
285
|
+
let(:data) do
|
286
|
+
{
|
287
|
+
'settings' => [
|
288
|
+
{
|
289
|
+
'name' => 'apm',
|
290
|
+
'enabled' => true,
|
291
|
+
'endpoint' => endpoint,
|
292
|
+
},
|
293
|
+
],
|
294
|
+
}
|
295
|
+
end
|
296
|
+
|
297
|
+
it "returns the endpoint" do
|
298
|
+
expect(described_class.new(project_id, data).apm_host).to eq(endpoint)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
context "and when an endpoint is NOT specified" do
|
303
|
+
let(:data) do
|
304
|
+
{
|
305
|
+
'settings' => [
|
306
|
+
{
|
307
|
+
'name' => 'apm',
|
308
|
+
'enabled' => true,
|
309
|
+
},
|
310
|
+
],
|
311
|
+
}
|
312
|
+
end
|
313
|
+
|
314
|
+
it "returns nil" do
|
315
|
+
expect(described_class.new(project_id, data).apm_host).to be_nil
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
context "when the 'apm' setting is missing" do
|
321
|
+
let(:data) do
|
322
|
+
{ 'settings' => [] }
|
323
|
+
end
|
324
|
+
|
325
|
+
it "returns nil" do
|
326
|
+
expect(described_class.new(project_id, data).apm_host).to be_nil
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
describe "#to_h" do
|
332
|
+
let(:data) do
|
333
|
+
{
|
334
|
+
'poll_sec' => 123,
|
335
|
+
'settings' => [
|
336
|
+
{
|
337
|
+
'name' => 'apm',
|
338
|
+
'enabled' => false,
|
339
|
+
},
|
340
|
+
],
|
341
|
+
}
|
342
|
+
end
|
343
|
+
|
344
|
+
subject { described_class.new(project_id, data) }
|
345
|
+
|
346
|
+
it "returns a hash representation of settings" do
|
347
|
+
expect(described_class.new(project_id, data).to_h).to eq(data)
|
348
|
+
end
|
349
|
+
|
350
|
+
it "doesn't allow mutation of the original data object" do
|
351
|
+
hash = subject.to_h
|
352
|
+
hash['poll_sec'] = 0
|
353
|
+
|
354
|
+
expect(subject.to_h).to eq(
|
355
|
+
'poll_sec' => 123,
|
356
|
+
'settings' => [
|
357
|
+
{
|
358
|
+
'name' => 'apm',
|
359
|
+
'enabled' => false,
|
360
|
+
},
|
361
|
+
],
|
362
|
+
)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
@@ -0,0 +1,230 @@
|
|
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(:config_path) { described_class::CONFIG_DUMP_PATH }
|
26
|
+
let(:config_dir) { File.dirname(config_path) }
|
27
|
+
|
28
|
+
let!(:stub) do
|
29
|
+
stub_request(:get, Regexp.new(endpoint))
|
30
|
+
.to_return(status: 200, body: body.to_json)
|
31
|
+
end
|
32
|
+
|
33
|
+
before do
|
34
|
+
# Do not create config dumps on disk.
|
35
|
+
allow(Dir).to receive(:mkdir).with(config_dir)
|
36
|
+
allow(File).to receive(:write).with(config_path, anything)
|
37
|
+
end
|
38
|
+
|
39
|
+
describe ".poll" do
|
40
|
+
describe "config loading" do
|
41
|
+
let(:settings_data) { described_class::SettingsData.new(project_id, body) }
|
42
|
+
|
43
|
+
before do
|
44
|
+
allow(File).to receive(:exist?).with(config_path).and_return(true)
|
45
|
+
allow(File).to receive(:read).with(config_path).and_return(body.to_json)
|
46
|
+
|
47
|
+
allow(described_class::SettingsData).to receive(:new).and_return(settings_data)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "loads the config from disk" do
|
51
|
+
expect(File).to receive(:read).with(config_path)
|
52
|
+
expect(settings_data).to receive(:merge!).with(body).twice
|
53
|
+
|
54
|
+
remote_settings = described_class.poll(project_id, host) {}
|
55
|
+
sleep(0.2)
|
56
|
+
remote_settings.stop_polling
|
57
|
+
|
58
|
+
expect(stub).to have_been_requested.once
|
59
|
+
end
|
60
|
+
|
61
|
+
it "yields the config to the block twice" do
|
62
|
+
block = proc {}
|
63
|
+
expect(block).to receive(:call).twice
|
64
|
+
|
65
|
+
remote_settings = described_class.poll(project_id, host, &block)
|
66
|
+
sleep(0.2)
|
67
|
+
remote_settings.stop_polling
|
68
|
+
|
69
|
+
expect(stub).to have_been_requested.once
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when config loading fails" do
|
73
|
+
it "logs an error" do
|
74
|
+
expect(File).to receive(:read).and_raise(StandardError)
|
75
|
+
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
76
|
+
'**Airbrake: config loading failed: StandardError',
|
77
|
+
)
|
78
|
+
|
79
|
+
remote_settings = described_class.poll(project_id, host) {}
|
80
|
+
sleep(0.2)
|
81
|
+
remote_settings.stop_polling
|
82
|
+
|
83
|
+
expect(stub).to have_been_requested.once
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when no errors are raised" do
|
89
|
+
it "makes a request to AWS S3" do
|
90
|
+
remote_settings = described_class.poll(project_id, host) {}
|
91
|
+
sleep(0.1)
|
92
|
+
remote_settings.stop_polling
|
93
|
+
|
94
|
+
expect(stub).to have_been_requested.at_least_once
|
95
|
+
end
|
96
|
+
|
97
|
+
it "sends params about the environment with the request" do
|
98
|
+
remote_settings = described_class.poll(project_id, host) {}
|
99
|
+
sleep(0.1)
|
100
|
+
remote_settings.stop_polling
|
101
|
+
|
102
|
+
stub_with_query_params = stub.with(
|
103
|
+
query: URI.decode_www_form(described_class::QUERY_PARAMS).to_h,
|
104
|
+
)
|
105
|
+
expect(stub_with_query_params).to have_been_requested.at_least_once
|
106
|
+
end
|
107
|
+
|
108
|
+
it "fetches remote settings" do
|
109
|
+
settings = nil
|
110
|
+
remote_settings = described_class.poll(project_id, host) do |data|
|
111
|
+
settings = data
|
112
|
+
end
|
113
|
+
sleep(0.1)
|
114
|
+
remote_settings.stop_polling
|
115
|
+
|
116
|
+
expect(settings.error_notifications?).to eq(true)
|
117
|
+
expect(settings.performance_stats?).to eq(false)
|
118
|
+
expect(settings.interval).to eq(1)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "when an error is raised while making a HTTP request" do
|
123
|
+
before do
|
124
|
+
allow(Net::HTTP).to receive(:get).and_raise(StandardError)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "doesn't fetch remote settings" do
|
128
|
+
settings = nil
|
129
|
+
remote_settings = described_class.poll(project_id, host) do |data|
|
130
|
+
settings = data
|
131
|
+
end
|
132
|
+
sleep(0.1)
|
133
|
+
remote_settings.stop_polling
|
134
|
+
|
135
|
+
expect(stub).not_to have_been_requested
|
136
|
+
expect(settings.interval).to eq(600)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "when an error is raised while parsing returned JSON" do
|
141
|
+
before do
|
142
|
+
allow(JSON).to receive(:parse).and_raise(JSON::ParserError)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "doesn't update settings data" do
|
146
|
+
settings = nil
|
147
|
+
remote_settings = described_class.poll(project_id, host) do |data|
|
148
|
+
settings = data
|
149
|
+
end
|
150
|
+
sleep(0.1)
|
151
|
+
remote_settings.stop_polling
|
152
|
+
|
153
|
+
expect(stub).to have_been_requested.once
|
154
|
+
expect(settings.interval).to eq(600)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "when API returns an XML response" do
|
159
|
+
let!(:stub) do
|
160
|
+
stub_request(:get, Regexp.new(endpoint))
|
161
|
+
.to_return(status: 200, body: '<?xml ...')
|
162
|
+
end
|
163
|
+
|
164
|
+
it "doesn't update settings data" do
|
165
|
+
settings = nil
|
166
|
+
remote_settings = described_class.poll(project_id, host) do |data|
|
167
|
+
settings = data
|
168
|
+
end
|
169
|
+
sleep(0.1)
|
170
|
+
remote_settings.stop_polling
|
171
|
+
|
172
|
+
expect(stub).to have_been_requested.once
|
173
|
+
expect(settings.interval).to eq(600)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "when a config route is specified in the returned data" do
|
178
|
+
let(:new_endpoint) do
|
179
|
+
"http://example.com"
|
180
|
+
end
|
181
|
+
|
182
|
+
let(:body) do
|
183
|
+
{ 'config_route' => new_endpoint, 'poll_sec' => 0.1 }
|
184
|
+
end
|
185
|
+
|
186
|
+
let!(:new_stub) do
|
187
|
+
stub_request(:get, Regexp.new(new_endpoint))
|
188
|
+
.to_return(status: 200, body: body.to_json)
|
189
|
+
end
|
190
|
+
|
191
|
+
it "makes the next request to the specified config route" do
|
192
|
+
remote_settings = described_class.poll(project_id, host) {}
|
193
|
+
sleep(0.2)
|
194
|
+
|
195
|
+
remote_settings.stop_polling
|
196
|
+
|
197
|
+
expect(stub).to have_been_requested.once
|
198
|
+
expect(new_stub).to have_been_requested.once
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "#stop_polling" do
|
204
|
+
it "dumps config data to disk" do
|
205
|
+
expect(Dir).to receive(:mkdir).with(config_dir)
|
206
|
+
expect(File).to receive(:write).with(config_path, body.to_json)
|
207
|
+
|
208
|
+
remote_settings = described_class.poll(project_id, host) {}
|
209
|
+
sleep(0.2)
|
210
|
+
remote_settings.stop_polling
|
211
|
+
|
212
|
+
expect(stub).to have_been_requested.once
|
213
|
+
end
|
214
|
+
|
215
|
+
context "when config dumping fails" do
|
216
|
+
it "logs an error" do
|
217
|
+
expect(File).to receive(:write).and_raise(StandardError)
|
218
|
+
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
219
|
+
'**Airbrake: config dumping failed: StandardError',
|
220
|
+
)
|
221
|
+
|
222
|
+
remote_settings = described_class.poll(project_id, host) {}
|
223
|
+
sleep(0.2)
|
224
|
+
remote_settings.stop_polling
|
225
|
+
|
226
|
+
expect(stub).to have_been_requested.once
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|