airbrake-ruby 4.14.0 → 5.0.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.
- checksums.yaml +4 -4
- data/lib/airbrake-ruby.rb +23 -35
- 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 +1 -0
- 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 +1 -2
- 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 +1 -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 +59 -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/filters/gem_root_filter_spec.rb +4 -4
- 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 +0 -25
- 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 +21 -12
@@ -7,18 +7,18 @@ RSpec.describe Airbrake::Filters::GemRootFilter do
|
|
7
7
|
after { 2.times { Gem.path.pop } }
|
8
8
|
|
9
9
|
it "replaces gem root in the backtrace with a label" do
|
10
|
-
# rubocop:disable
|
10
|
+
# rubocop:disable Layout/LineLength
|
11
11
|
notice[:errors].first[:backtrace] = [
|
12
12
|
{ file: "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb" },
|
13
13
|
{ file: "#{root1}/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb" },
|
14
14
|
{ file: "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb" },
|
15
15
|
{ file: "#{root2}/gems/rspec-core-3.3.2/exe/rspec" },
|
16
16
|
]
|
17
|
-
# rubocop:enable
|
17
|
+
# rubocop:enable Layout/LineLength
|
18
18
|
|
19
19
|
subject.call(notice)
|
20
20
|
|
21
|
-
# rubocop:disable
|
21
|
+
# rubocop:disable Layout/LineLength
|
22
22
|
expect(notice[:errors].first[:backtrace]).to(
|
23
23
|
eq(
|
24
24
|
[
|
@@ -29,7 +29,7 @@ RSpec.describe Airbrake::Filters::GemRootFilter do
|
|
29
29
|
],
|
30
30
|
),
|
31
31
|
)
|
32
|
-
# rubocop:enable
|
32
|
+
# rubocop:enable Layout/LineLength
|
33
33
|
end
|
34
34
|
|
35
35
|
it "does not filter file when it is nil" do
|
@@ -1,4 +1,4 @@
|
|
1
|
-
RSpec.describe Airbrake::Filters::
|
1
|
+
RSpec.describe Airbrake::Filters::KeysAllowlist do
|
2
2
|
subject { described_class.new(patterns) }
|
3
3
|
|
4
4
|
let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
|
@@ -70,10 +70,10 @@ RSpec.describe Airbrake::Filters::KeysWhitelist do
|
|
70
70
|
|
71
71
|
it "logs an error" do
|
72
72
|
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
73
|
-
/
|
73
|
+
/KeysAllowlist is invalid.+patterns: \[#<Object:.+>\]/,
|
74
74
|
)
|
75
|
-
|
76
|
-
|
75
|
+
keys_allowlist = described_class.new(patterns)
|
76
|
+
keys_allowlist.call(notice)
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -83,10 +83,10 @@ RSpec.describe Airbrake::Filters::KeysWhitelist do
|
|
83
83
|
context "and when the filter is called once" do
|
84
84
|
it "logs an error" do
|
85
85
|
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
86
|
-
/
|
86
|
+
/KeysAllowlist is invalid.+patterns: \[#<Proc:.+>\]/,
|
87
87
|
)
|
88
|
-
|
89
|
-
|
88
|
+
keys_allowlist = described_class.new(patterns)
|
89
|
+
keys_allowlist.call(notice)
|
90
90
|
end
|
91
91
|
|
92
92
|
include_examples(
|
@@ -113,10 +113,10 @@ RSpec.describe Airbrake::Filters::KeysWhitelist do
|
|
113
113
|
|
114
114
|
it "logs an error" do
|
115
115
|
expect(Airbrake::Loggable.instance).to receive(:error).with(
|
116
|
-
/
|
116
|
+
/KeysAllowlist is invalid.+patterns: \[#<Object:.+>\]/,
|
117
117
|
)
|
118
|
-
|
119
|
-
|
118
|
+
keys_allowlist = described_class.new(patterns)
|
119
|
+
keys_allowlist.call(notice)
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
@@ -151,6 +151,7 @@ RSpec.describe Airbrake::Filters::KeysWhitelist do
|
|
151
151
|
# thing. One is a Java exception, the other is a Ruby exception.
|
152
152
|
# Likely a bug: https://github.com/jruby/jruby/issues/1903
|
153
153
|
raise ex unless RUBY_ENGINE == 'jruby'
|
154
|
+
|
154
155
|
expect { subject.call(notice) }.to raise_error(java.lang.StackOverflowError)
|
155
156
|
end
|
156
157
|
end
|
@@ -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
|
|
@@ -150,6 +150,16 @@ RSpec.describe Airbrake::Filters::KeysBlacklist do
|
|
150
150
|
{ bongo: { bish: '[Filtered]' } },
|
151
151
|
],
|
152
152
|
)
|
153
|
+
|
154
|
+
it "doesn't mutate the original hash" do
|
155
|
+
params = { bongo: { bish: 'bash' } }
|
156
|
+
notice[:params] = params
|
157
|
+
|
158
|
+
blocklist = described_class.new([:bish])
|
159
|
+
blocklist.call(notice)
|
160
|
+
|
161
|
+
expect(params[:bongo][:bish]).to eq('bash')
|
162
|
+
end
|
153
163
|
end
|
154
164
|
|
155
165
|
context "and it is recursive" do
|
@@ -5,18 +5,18 @@ RSpec.describe Airbrake::Filters::RootDirectoryFilter do
|
|
5
5
|
let(:notice) { Airbrake::Notice.new(AirbrakeTestError.new) }
|
6
6
|
|
7
7
|
it "replaces root directory in the backtrace with a label" do
|
8
|
-
# rubocop:disable
|
8
|
+
# rubocop:disable Layout/LineLength
|
9
9
|
notice[:errors].first[:backtrace] = [
|
10
10
|
{ file: "/home/kyrylo/code/airbrake/ruby/spec/spec_helper.rb" },
|
11
11
|
{ file: "#{root_directory}/gems/rspec-core-3.3.2/lib/rspec/core/configuration.rb " },
|
12
12
|
{ file: "/opt/rubies/ruby-2.2.2/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb" },
|
13
13
|
{ file: "#{root_directory}/gems/rspec-core-3.3.2/exe/rspec" },
|
14
14
|
]
|
15
|
-
# rubocop:enable
|
15
|
+
# rubocop:enable Layout/LineLength
|
16
16
|
|
17
17
|
subject.call(notice)
|
18
18
|
|
19
|
-
# rubocop:disable
|
19
|
+
# rubocop:disable Layout/LineLength
|
20
20
|
expect(notice[:errors].first[:backtrace]).to(
|
21
21
|
eq(
|
22
22
|
[
|
@@ -27,7 +27,7 @@ RSpec.describe Airbrake::Filters::RootDirectoryFilter do
|
|
27
27
|
],
|
28
28
|
),
|
29
29
|
)
|
30
|
-
# rubocop:enable
|
30
|
+
# rubocop:enable Layout/LineLength
|
31
31
|
end
|
32
32
|
|
33
33
|
it "does not filter file when it is nil" do
|
@@ -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)
|
@@ -22,7 +22,7 @@ RSpec.describe Airbrake::Filters::SqlFilter do
|
|
22
22
|
|
23
23
|
ALL_DIALECTS = %i[mysql postgres sqlite cassandra oracle].freeze
|
24
24
|
|
25
|
-
# rubocop:disable
|
25
|
+
# rubocop:disable Layout/LineLength
|
26
26
|
[
|
27
27
|
{
|
28
28
|
input: 'SELECT * FROM things;',
|
@@ -229,7 +229,7 @@ RSpec.describe Airbrake::Filters::SqlFilter do
|
|
229
229
|
].each do |test|
|
230
230
|
include_examples 'query filtering', test
|
231
231
|
end
|
232
|
-
# rubocop:enable
|
232
|
+
# rubocop:enable Layout/LineLength
|
233
233
|
|
234
234
|
[
|
235
235
|
'COMMIT',
|
@@ -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
|
@@ -149,10 +149,10 @@ RSpec.describe Airbrake::NoticeNotifier do
|
|
149
149
|
expect(proxied_request.header['proxy-authorization'].first)
|
150
150
|
.to eq('Basic dXNlcjpwYXNzd29yZA==')
|
151
151
|
|
152
|
-
# rubocop:disable
|
152
|
+
# rubocop:disable Layout/LineLength
|
153
153
|
expect(proxied_request.request_line)
|
154
154
|
.to eq("POST http://localhost:#{proxy.config[:Port]}/api/v3/projects/105138/notices HTTP/1.1\r\n")
|
155
|
-
# rubocop:enable
|
155
|
+
# rubocop:enable Layout/LineLength
|
156
156
|
end
|
157
157
|
end
|
158
158
|
|
@@ -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
|
|
@@ -309,7 +309,7 @@ RSpec.describe Airbrake::NoticeNotifier do
|
|
309
309
|
|
310
310
|
notice = subject.build_notice(Exception.new)
|
311
311
|
|
312
|
-
# rubocop:disable
|
312
|
+
# rubocop:disable Layout/LineLength
|
313
313
|
expect(notice[:errors].first[:backtrace]).to eq(
|
314
314
|
[
|
315
315
|
{ file: 'org/jruby/RubyKernel.java', line: 998, function: 'eval' },
|
@@ -317,7 +317,7 @@ RSpec.describe Airbrake::NoticeNotifier do
|
|
317
317
|
{ file: '/ruby/stdlib/irb.rb:489', line: 489, function: 'block in eval_input' },
|
318
318
|
],
|
319
319
|
)
|
320
|
-
# rubocop:enable
|
320
|
+
# rubocop:enable Layout/LineLength
|
321
321
|
end
|
322
322
|
end
|
323
323
|
|
data/spec/notice_spec.rb
CHANGED
@@ -268,7 +268,7 @@ RSpec.describe Airbrake::Notice do
|
|
268
268
|
it "sets a payload value" do
|
269
269
|
hash = { bingo: 'bango' }
|
270
270
|
notice[:params] = hash
|
271
|
-
expect(notice[:params]).to
|
271
|
+
expect(notice[:params]).to eq(hash)
|
272
272
|
end
|
273
273
|
|
274
274
|
it "raises error if notice is ignored" do
|
@@ -3,21 +3,9 @@ RSpec.describe Airbrake::PerformanceBreakdown do
|
|
3
3
|
subject do
|
4
4
|
described_class.new(
|
5
5
|
method: 'GET', route: '/', response_type: '', groups: {},
|
6
|
-
start_time: Time.now
|
7
6
|
)
|
8
7
|
end
|
9
8
|
|
10
9
|
it { is_expected.to respond_to(:stash) }
|
11
10
|
end
|
12
|
-
|
13
|
-
describe "#end_time" do
|
14
|
-
it "is always equal to start_time + 1 second by default" do
|
15
|
-
time = Time.now
|
16
|
-
performance_breakdown = described_class.new(
|
17
|
-
method: 'GET', route: '/', response_type: '', groups: {},
|
18
|
-
start_time: time
|
19
|
-
)
|
20
|
-
expect(performance_breakdown.end_time).to eq(time + 1)
|
21
|
-
end
|
22
|
-
end
|
23
11
|
end
|
@@ -541,31 +541,6 @@ RSpec.describe Airbrake::PerformanceNotifier do
|
|
541
541
|
end
|
542
542
|
end
|
543
543
|
|
544
|
-
context "when :start_time is specified (deprecated)" do
|
545
|
-
before do
|
546
|
-
allow(Kernel).to receive(:warn)
|
547
|
-
end
|
548
|
-
|
549
|
-
it "uses the value of :start_time to update stat" do
|
550
|
-
subject.notify(
|
551
|
-
Airbrake::Query.new(
|
552
|
-
method: 'POST',
|
553
|
-
route: '/foo',
|
554
|
-
query: 'SELECT * FROM things',
|
555
|
-
start_time: Time.new(2018, 1, 1, 0, 49, 0, 0),
|
556
|
-
end_time: Time.new(2018, 1, 1, 0, 50, 0, 0),
|
557
|
-
),
|
558
|
-
)
|
559
|
-
subject.close
|
560
|
-
|
561
|
-
expect(
|
562
|
-
a_request(:put, queries).with(
|
563
|
-
body: /"count":1,"sum":60000.0,"sumsq":3600000000.0/,
|
564
|
-
),
|
565
|
-
).to have_been_made
|
566
|
-
end
|
567
|
-
end
|
568
|
-
|
569
544
|
context "when provided :timing is zero" do
|
570
545
|
it "doesn't notify" do
|
571
546
|
queue = Airbrake::Queue.new(queue: 'bananas', error_count: 0, timing: 0)
|
data/spec/query_spec.rb
CHANGED
@@ -2,20 +2,10 @@ RSpec.describe Airbrake::Query do
|
|
2
2
|
describe "#stash" do
|
3
3
|
subject do
|
4
4
|
described_class.new(
|
5
|
-
method: 'GET', route: '/', query: '',
|
5
|
+
method: 'GET', route: '/', query: '',
|
6
6
|
)
|
7
7
|
end
|
8
8
|
|
9
9
|
it { is_expected.to respond_to(:stash) }
|
10
10
|
end
|
11
|
-
|
12
|
-
describe "#end_time" do
|
13
|
-
it "is always equal to start_time + 1 second by default" do
|
14
|
-
time = Time.now
|
15
|
-
query = described_class.new(
|
16
|
-
method: 'GET', route: '/', query: '', start_time: time,
|
17
|
-
)
|
18
|
-
expect(query.end_time).to eq(time + 1)
|
19
|
-
end
|
20
|
-
end
|
21
11
|
end
|
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
|