airbrake-ruby 4.13.3-java → 5.0.0.rc.1-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/config.rb +65 -13
- data/lib/airbrake-ruby/config/processor.rb +80 -0
- data/lib/airbrake-ruby/config/validator.rb +4 -0
- data/lib/airbrake-ruby/filter_chain.rb +15 -1
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +2 -1
- 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 +5 -5
- data/lib/airbrake-ruby/notice_notifier.rb +6 -0
- data/lib/airbrake-ruby/performance_notifier.rb +1 -1
- data/lib/airbrake-ruby/remote_settings.rb +128 -0
- data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
- data/lib/airbrake-ruby/sync_sender.rb +1 -1
- data/lib/airbrake-ruby/thread_pool.rb +1 -0
- data/lib/airbrake-ruby/version.rb +1 -1
- data/spec/airbrake_spec.rb +71 -36
- data/spec/config/processor_spec.rb +223 -0
- data/spec/config/validator_spec.rb +18 -1
- data/spec/config_spec.rb +38 -6
- data/spec/filter_chain_spec.rb +27 -0
- data/spec/filters/git_last_checkout_filter_spec.rb +20 -3
- data/spec/filters/{keys_whitelist_spec.rb → keys_allowlist_spec.rb} +10 -10
- data/spec/filters/{keys_blacklist_spec.rb → keys_blocklist_spec.rb} +10 -10
- data/spec/filters/sql_filter_spec.rb +3 -3
- data/spec/notice_notifier/options_spec.rb +4 -4
- data/spec/performance_notifier_spec.rb +2 -2
- data/spec/remote_settings/settings_data_spec.rb +327 -0
- data/spec/remote_settings_spec.rb +212 -0
- data/spec/sync_sender_spec.rb +3 -1
- data/spec/thread_pool_spec.rb +25 -5
- metadata +20 -12
@@ -1,4 +1,4 @@
|
|
1
|
-
RSpec.describe Airbrake::Filters::
|
1
|
+
RSpec.describe Airbrake::Filters::KeysBlocklist do
|
2
2
|
subject { described_class.new(patterns) }
|
3
3
|
|
4
4
|
let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
|
@@ -91,10 +91,10 @@ RSpec.describe Airbrake::Filters::KeysBlacklist do
|
|
91
91
|
|
92
92
|
it "logs an error" do
|
93
93
|
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
94
|
-
/
|
94
|
+
/KeysBlocklist is invalid.+patterns: \[#<Object:.+>\]/,
|
95
95
|
)
|
96
|
-
|
97
|
-
|
96
|
+
keys_blocklist = described_class.new(patterns)
|
97
|
+
keys_blocklist.call(notice)
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
@@ -104,10 +104,10 @@ RSpec.describe Airbrake::Filters::KeysBlacklist do
|
|
104
104
|
context "and when the filter is called once" do
|
105
105
|
it "logs an error" do
|
106
106
|
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
107
|
-
/
|
107
|
+
/KeysBlocklist is invalid.+patterns: \[#<Proc:.+>\]/,
|
108
108
|
)
|
109
|
-
|
110
|
-
|
109
|
+
keys_blocklist = described_class.new(patterns)
|
110
|
+
keys_blocklist.call(notice)
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
@@ -133,10 +133,10 @@ RSpec.describe Airbrake::Filters::KeysBlacklist do
|
|
133
133
|
|
134
134
|
it "logs an error" do
|
135
135
|
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
136
|
-
/
|
136
|
+
/KeysBlocklist is invalid.+patterns: \[#<Object:.+>\]/,
|
137
137
|
)
|
138
|
-
|
139
|
-
|
138
|
+
keys_blocklist = described_class.new(patterns)
|
139
|
+
keys_blocklist.call(notice)
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
@@ -10,7 +10,7 @@ RSpec.describe Airbrake::Filters::SqlFilter do
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
shared_examples "query
|
13
|
+
shared_examples "query blocklisting" do |query, opts|
|
14
14
|
it "ignores '#{query}'" do
|
15
15
|
filter = described_class.new('postgres')
|
16
16
|
q = Airbrake::Query.new(query: query, method: 'GET', route: '/', timing: 1)
|
@@ -263,12 +263,12 @@ RSpec.describe Airbrake::Filters::SqlFilter do
|
|
263
263
|
|
264
264
|
'SELECT t.oid, t.typname FROM pg_type as t WHERE t.typname IN (?)',
|
265
265
|
].each do |query|
|
266
|
-
include_examples 'query
|
266
|
+
include_examples 'query blocklisting', query, should_ignore: true
|
267
267
|
end
|
268
268
|
|
269
269
|
[
|
270
270
|
'UPDATE "users" SET "last_sign_in_at" = ? WHERE "users"."id" = ?',
|
271
271
|
].each do |query|
|
272
|
-
include_examples 'query
|
272
|
+
include_examples 'query blocklisting', query, should_ignore: false
|
273
273
|
end
|
274
274
|
end
|
@@ -234,14 +234,14 @@ RSpec.describe Airbrake::NoticeNotifier do
|
|
234
234
|
end
|
235
235
|
end
|
236
236
|
|
237
|
-
describe ":
|
237
|
+
describe ":blocklist_keys" do
|
238
238
|
# Fixes https://github.com/airbrake/airbrake-ruby/issues/276
|
239
|
-
context "when specified along with :
|
239
|
+
context "when specified along with :allowlist_keys" do
|
240
240
|
context "and when context payload is present" do
|
241
241
|
before do
|
242
242
|
Airbrake::Config.instance.merge(
|
243
|
-
|
244
|
-
|
243
|
+
blocklist_keys: %i[password password_confirmation],
|
244
|
+
allowlist_keys: [:email, /user/i, 'account_id'],
|
245
245
|
)
|
246
246
|
end
|
247
247
|
|
@@ -389,7 +389,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
|
|
389
389
|
subject.close
|
390
390
|
|
391
391
|
expect(promise).to be_an(Airbrake::Promise)
|
392
|
-
expect(promise.value).to eq('' =>
|
392
|
+
expect(promise.value).to eq('' => '')
|
393
393
|
end
|
394
394
|
|
395
395
|
it "checks performance stat configuration" do
|
@@ -601,7 +601,7 @@ RSpec.describe Airbrake::PerformanceNotifier do
|
|
601
601
|
body: %r|\A{"queries":\[{"method":"POST","route":"/foo"|,
|
602
602
|
),
|
603
603
|
).to have_been_made
|
604
|
-
expect(retval).to eq('' =>
|
604
|
+
expect(retval).to eq('' => '')
|
605
605
|
end
|
606
606
|
end
|
607
607
|
|
@@ -0,0 +1,327 @@
|
|
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
|
+
# rubocop:disable Performance/RedundantMerge
|
13
|
+
settings_data.merge!('poll_sec' => 123, 'config_route' => 'abc')
|
14
|
+
# rubocop:enable Performance/RedundantMerge
|
15
|
+
|
16
|
+
expect(settings_data.interval).to eq(123)
|
17
|
+
expect(settings_data.config_route).to eq('abc')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#interval" do
|
22
|
+
context "when given data has zero interval" do
|
23
|
+
let(:data) do
|
24
|
+
{ 'poll_sec' => 0 }
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns the default interval" do
|
28
|
+
expect(described_class.new(project_id, data).interval).to eq(600)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when given data has negative interval" do
|
33
|
+
let(:data) do
|
34
|
+
{ 'poll_sec' => -1 }
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns the default interval" do
|
38
|
+
expect(described_class.new(project_id, data).interval).to eq(600)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when given data has nil interval" do
|
43
|
+
let(:data) do
|
44
|
+
{ 'poll_sec' => nil }
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns the default interval" do
|
48
|
+
expect(described_class.new(project_id, data).interval).to eq(600)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "when given data has a positive interval" do
|
53
|
+
let(:data) do
|
54
|
+
{ 'poll_sec' => 123 }
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns the interval from data" do
|
58
|
+
expect(described_class.new(project_id, data).interval).to eq(123)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#config_route" do
|
64
|
+
context "when given a pathname" do
|
65
|
+
let(:data) do
|
66
|
+
{ 'config_route' => 'http://example.com' }
|
67
|
+
end
|
68
|
+
|
69
|
+
it "returns the given pathname" do
|
70
|
+
expect(described_class.new(project_id, data).config_route)
|
71
|
+
.to eq('http://example.com')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "when the pathname is nil" do
|
76
|
+
let(:data) do
|
77
|
+
{ 'config_route' => nil }
|
78
|
+
end
|
79
|
+
|
80
|
+
it "returns the default pathname" do
|
81
|
+
expect(described_class.new(project_id, data).config_route).to eq(
|
82
|
+
'https://staging-notifier-configs.s3.amazonaws.com/' \
|
83
|
+
"2020-06-18/config/#{project_id}/config.json",
|
84
|
+
)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#error_notifications?" do
|
90
|
+
context "when the 'errors' setting is present" do
|
91
|
+
context "and when it is enabled" do
|
92
|
+
let(:data) do
|
93
|
+
{
|
94
|
+
'settings' => [
|
95
|
+
{
|
96
|
+
'name' => 'errors',
|
97
|
+
'enabled' => true,
|
98
|
+
},
|
99
|
+
],
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
it "returns true" do
|
104
|
+
expect(described_class.new(project_id, data).error_notifications?)
|
105
|
+
.to eq(true)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "and when it is disabled" do
|
110
|
+
let(:data) do
|
111
|
+
{
|
112
|
+
'settings' => [
|
113
|
+
{
|
114
|
+
'name' => 'errors',
|
115
|
+
'enabled' => false,
|
116
|
+
},
|
117
|
+
],
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
it "returns false" do
|
122
|
+
expect(described_class.new(project_id, data).error_notifications?)
|
123
|
+
.to eq(false)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "when the 'errors' setting is missing" do
|
129
|
+
let(:data) do
|
130
|
+
{ 'settings' => [] }
|
131
|
+
end
|
132
|
+
|
133
|
+
it "returns true" do
|
134
|
+
expect(described_class.new(project_id, data).error_notifications?)
|
135
|
+
.to eq(true)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#performance_stats?" do
|
141
|
+
context "when the 'apm' setting is present" do
|
142
|
+
context "and when it is enabled" do
|
143
|
+
let(:data) do
|
144
|
+
{
|
145
|
+
'settings' => [
|
146
|
+
{
|
147
|
+
'name' => 'apm',
|
148
|
+
'enabled' => true,
|
149
|
+
},
|
150
|
+
],
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
it "returns true" do
|
155
|
+
expect(described_class.new(project_id, data).performance_stats?)
|
156
|
+
.to eq(true)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "and when it is disabled" do
|
161
|
+
let(:data) do
|
162
|
+
{
|
163
|
+
'settings' => [
|
164
|
+
{
|
165
|
+
'name' => 'apm',
|
166
|
+
'enabled' => false,
|
167
|
+
},
|
168
|
+
],
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
it "returns false" do
|
173
|
+
expect(described_class.new(project_id, data).performance_stats?)
|
174
|
+
.to eq(false)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context "when the 'apm' setting is missing" do
|
180
|
+
let(:data) do
|
181
|
+
{ 'settings' => [] }
|
182
|
+
end
|
183
|
+
|
184
|
+
it "returns true" do
|
185
|
+
expect(described_class.new(project_id, data).performance_stats?)
|
186
|
+
.to eq(true)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "#error_host" do
|
192
|
+
context "when the 'errors' setting is present" do
|
193
|
+
context "and when 'endpoint' is specified" do
|
194
|
+
let(:endpoint) { 'https://api.example.com/' }
|
195
|
+
|
196
|
+
let(:data) do
|
197
|
+
{
|
198
|
+
'settings' => [
|
199
|
+
{
|
200
|
+
'name' => 'errors',
|
201
|
+
'enabled' => true,
|
202
|
+
'endpoint' => endpoint,
|
203
|
+
},
|
204
|
+
],
|
205
|
+
}
|
206
|
+
end
|
207
|
+
|
208
|
+
it "returns the endpoint" do
|
209
|
+
expect(described_class.new(project_id, data).error_host).to eq(endpoint)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "and when an endpoint is NOT specified" do
|
214
|
+
let(:data) do
|
215
|
+
{
|
216
|
+
'settings' => [
|
217
|
+
{
|
218
|
+
'name' => 'errors',
|
219
|
+
'enabled' => true,
|
220
|
+
},
|
221
|
+
],
|
222
|
+
}
|
223
|
+
end
|
224
|
+
|
225
|
+
it "returns nil" do
|
226
|
+
expect(described_class.new(project_id, data).error_host).to be_nil
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "when the 'errors' setting is missing" do
|
232
|
+
let(:data) do
|
233
|
+
{ 'settings' => [] }
|
234
|
+
end
|
235
|
+
|
236
|
+
it "returns nil" do
|
237
|
+
expect(described_class.new(project_id, data).error_host).to be_nil
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
describe "#apm_host" do
|
243
|
+
context "when the 'apm' setting is present" do
|
244
|
+
context "and when 'endpoint' is specified" do
|
245
|
+
let(:endpoint) { 'https://api.example.com/' }
|
246
|
+
|
247
|
+
let(:data) do
|
248
|
+
{
|
249
|
+
'settings' => [
|
250
|
+
{
|
251
|
+
'name' => 'apm',
|
252
|
+
'enabled' => true,
|
253
|
+
'endpoint' => endpoint,
|
254
|
+
},
|
255
|
+
],
|
256
|
+
}
|
257
|
+
end
|
258
|
+
|
259
|
+
it "returns the endpoint" do
|
260
|
+
expect(described_class.new(project_id, data).apm_host).to eq(endpoint)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
context "and when an endpoint is NOT specified" do
|
265
|
+
let(:data) do
|
266
|
+
{
|
267
|
+
'settings' => [
|
268
|
+
{
|
269
|
+
'name' => 'apm',
|
270
|
+
'enabled' => true,
|
271
|
+
},
|
272
|
+
],
|
273
|
+
}
|
274
|
+
end
|
275
|
+
|
276
|
+
it "returns nil" do
|
277
|
+
expect(described_class.new(project_id, data).apm_host).to be_nil
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
context "when the 'apm' setting is missing" do
|
283
|
+
let(:data) do
|
284
|
+
{ 'settings' => [] }
|
285
|
+
end
|
286
|
+
|
287
|
+
it "returns nil" do
|
288
|
+
expect(described_class.new(project_id, data).apm_host).to be_nil
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe "#to_h" do
|
294
|
+
let(:data) do
|
295
|
+
{
|
296
|
+
'poll_sec' => 123,
|
297
|
+
'settings' => [
|
298
|
+
{
|
299
|
+
'name' => 'apm',
|
300
|
+
'enabled' => false,
|
301
|
+
},
|
302
|
+
],
|
303
|
+
}
|
304
|
+
end
|
305
|
+
|
306
|
+
subject { described_class.new(project_id, data) }
|
307
|
+
|
308
|
+
it "returns a hash representation of settings" do
|
309
|
+
expect(described_class.new(project_id, data).to_h).to eq(data)
|
310
|
+
end
|
311
|
+
|
312
|
+
it "doesn't allow mutation of the original data object" do
|
313
|
+
hash = subject.to_h
|
314
|
+
hash['poll_sec'] = 0
|
315
|
+
|
316
|
+
expect(subject.to_h).to eq(
|
317
|
+
'poll_sec' => 123,
|
318
|
+
'settings' => [
|
319
|
+
{
|
320
|
+
'name' => 'apm',
|
321
|
+
'enabled' => false,
|
322
|
+
},
|
323
|
+
],
|
324
|
+
)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
RSpec.describe Airbrake::RemoteSettings do
|
2
|
+
let(:project_id) { 123 }
|
3
|
+
|
4
|
+
let(:endpoint) do
|
5
|
+
"https://staging-notifier-configs.s3.amazonaws.com/2020-06-18/config/" \
|
6
|
+
"#{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
|
+
before do
|
29
|
+
stub_request(:get, endpoint).to_return(status: 200, body: body.to_json)
|
30
|
+
|
31
|
+
# Do not create config dumps on disk.
|
32
|
+
allow(Dir).to receive(:mkdir).with(config_dir)
|
33
|
+
allow(File).to receive(:write).with(config_path, anything)
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ".poll" do
|
37
|
+
describe "config loading" do
|
38
|
+
let(:settings_data) { described_class::SettingsData.new(project_id, body) }
|
39
|
+
|
40
|
+
before do
|
41
|
+
allow(File).to receive(:exist?).with(config_path).and_return(true)
|
42
|
+
allow(File).to receive(:read).with(config_path).and_return(body.to_json)
|
43
|
+
|
44
|
+
allow(described_class::SettingsData).to receive(:new).and_return(settings_data)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "loads the config from disk" do
|
48
|
+
expect(File).to receive(:read).with(config_path)
|
49
|
+
expect(settings_data).to receive(:merge!).with(body).twice
|
50
|
+
|
51
|
+
remote_settings = described_class.poll(project_id) {}
|
52
|
+
sleep(0.2)
|
53
|
+
remote_settings.stop_polling
|
54
|
+
|
55
|
+
expect(a_request(:get, endpoint)).to have_been_made.once
|
56
|
+
end
|
57
|
+
|
58
|
+
it "yields the config to the block twice" do
|
59
|
+
block = proc {}
|
60
|
+
expect(block).to receive(:call).twice
|
61
|
+
|
62
|
+
remote_settings = described_class.poll(project_id, &block)
|
63
|
+
sleep(0.2)
|
64
|
+
remote_settings.stop_polling
|
65
|
+
|
66
|
+
expect(a_request(:get, endpoint)).to have_been_made.once
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when config loading fails" do
|
70
|
+
it "logs an error" do
|
71
|
+
expect(File).to receive(:read).and_raise(StandardError)
|
72
|
+
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
73
|
+
'**Airbrake: config loading failed: StandardError',
|
74
|
+
)
|
75
|
+
|
76
|
+
remote_settings = described_class.poll(project_id) {}
|
77
|
+
sleep(0.2)
|
78
|
+
remote_settings.stop_polling
|
79
|
+
|
80
|
+
expect(a_request(:get, endpoint)).to have_been_made.once
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when no errors are raised" do
|
86
|
+
it "makes a request to AWS S3" do
|
87
|
+
remote_settings = described_class.poll(project_id) {}
|
88
|
+
sleep(0.1)
|
89
|
+
remote_settings.stop_polling
|
90
|
+
|
91
|
+
expect(a_request(:get, endpoint)).to have_been_made.at_least_once
|
92
|
+
end
|
93
|
+
|
94
|
+
it "fetches remote settings" do
|
95
|
+
settings = nil
|
96
|
+
remote_settings = described_class.poll(project_id) do |data|
|
97
|
+
settings = data
|
98
|
+
end
|
99
|
+
sleep(0.1)
|
100
|
+
remote_settings.stop_polling
|
101
|
+
|
102
|
+
expect(settings.error_notifications?).to eq(true)
|
103
|
+
expect(settings.performance_stats?).to eq(false)
|
104
|
+
expect(settings.interval).to eq(1)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "when an error is raised while making a HTTP request" do
|
109
|
+
before do
|
110
|
+
allow(Net::HTTP).to receive(:get).and_raise(StandardError)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "doesn't fetch remote settings" do
|
114
|
+
settings = nil
|
115
|
+
remote_settings = described_class.poll(project_id) do |data|
|
116
|
+
settings = data
|
117
|
+
end
|
118
|
+
sleep(0.1)
|
119
|
+
remote_settings.stop_polling
|
120
|
+
|
121
|
+
expect(a_request(:get, endpoint)).not_to have_been_made
|
122
|
+
expect(settings.interval).to eq(600)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when an error is raised while parsing returned JSON" do
|
127
|
+
before do
|
128
|
+
allow(JSON).to receive(:parse).and_raise(JSON::ParserError)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "doesn't update settings data" do
|
132
|
+
settings = nil
|
133
|
+
remote_settings = described_class.poll(project_id) do |data|
|
134
|
+
settings = data
|
135
|
+
end
|
136
|
+
sleep(0.1)
|
137
|
+
remote_settings.stop_polling
|
138
|
+
|
139
|
+
expect(a_request(:get, endpoint)).to have_been_made.once
|
140
|
+
expect(settings.interval).to eq(600)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "when API returns an XML response" do
|
145
|
+
before do
|
146
|
+
stub_request(:get, endpoint).to_return(status: 200, body: '<?xml ...')
|
147
|
+
end
|
148
|
+
|
149
|
+
it "doesn't update settings data" do
|
150
|
+
settings = nil
|
151
|
+
remote_settings = described_class.poll(project_id) do |data|
|
152
|
+
settings = data
|
153
|
+
end
|
154
|
+
sleep(0.1)
|
155
|
+
remote_settings.stop_polling
|
156
|
+
|
157
|
+
expect(a_request(:get, endpoint)).to have_been_made.once
|
158
|
+
expect(settings.interval).to eq(600)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "when a config route is specified in the returned data" do
|
163
|
+
let(:new_endpoint) { 'http://example.com' }
|
164
|
+
|
165
|
+
let(:body) do
|
166
|
+
{ 'config_route' => new_endpoint, 'poll_sec' => 0.1 }
|
167
|
+
end
|
168
|
+
|
169
|
+
before do
|
170
|
+
stub_request(:get, new_endpoint).to_return(status: 200, body: body.to_json)
|
171
|
+
end
|
172
|
+
|
173
|
+
it "makes the next request to the specified config route" do
|
174
|
+
remote_settings = described_class.poll(project_id) {}
|
175
|
+
sleep(0.2)
|
176
|
+
|
177
|
+
remote_settings.stop_polling
|
178
|
+
|
179
|
+
expect(a_request(:get, endpoint)).to have_been_made.once
|
180
|
+
expect(a_request(:get, new_endpoint)).to have_been_made.once
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "#stop_polling" do
|
186
|
+
it "dumps config data to disk" do
|
187
|
+
expect(Dir).to receive(:mkdir).with(config_dir)
|
188
|
+
expect(File).to receive(:write).with(config_path, body.to_json)
|
189
|
+
|
190
|
+
remote_settings = described_class.poll(project_id) {}
|
191
|
+
sleep(0.2)
|
192
|
+
remote_settings.stop_polling
|
193
|
+
|
194
|
+
expect(a_request(:get, endpoint)).to have_been_made.once
|
195
|
+
end
|
196
|
+
|
197
|
+
context "when config dumping fails" do
|
198
|
+
it "logs an error" do
|
199
|
+
expect(File).to receive(:write).and_raise(StandardError)
|
200
|
+
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
201
|
+
'**Airbrake: config dumping failed: StandardError',
|
202
|
+
)
|
203
|
+
|
204
|
+
remote_settings = described_class.poll(project_id) {}
|
205
|
+
sleep(0.2)
|
206
|
+
remote_settings.stop_polling
|
207
|
+
|
208
|
+
expect(a_request(:get, endpoint)).to have_been_made.once
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|