airbrake-ruby 4.15.0-java → 5.0.2-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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +22 -33
  3. data/lib/airbrake-ruby/async_sender.rb +1 -1
  4. data/lib/airbrake-ruby/backtrace.rb +6 -5
  5. data/lib/airbrake-ruby/config.rb +30 -30
  6. data/lib/airbrake-ruby/config/processor.rb +69 -0
  7. data/lib/airbrake-ruby/config/validator.rb +6 -0
  8. data/lib/airbrake-ruby/file_cache.rb +1 -1
  9. data/lib/airbrake-ruby/filter_chain.rb +1 -0
  10. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  11. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  12. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +1 -2
  13. data/lib/airbrake-ruby/filters/git_repository_filter.rb +3 -0
  14. data/lib/airbrake-ruby/filters/git_revision_filter.rb +2 -0
  15. data/lib/airbrake-ruby/filters/keys_filter.rb +21 -13
  16. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  17. data/lib/airbrake-ruby/filters/sql_filter.rb +4 -4
  18. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  19. data/lib/airbrake-ruby/filters/thread_filter.rb +2 -0
  20. data/lib/airbrake-ruby/ignorable.rb +1 -0
  21. data/lib/airbrake-ruby/notice.rb +1 -8
  22. data/lib/airbrake-ruby/notice_notifier.rb +1 -0
  23. data/lib/airbrake-ruby/performance_breakdown.rb +1 -6
  24. data/lib/airbrake-ruby/performance_notifier.rb +2 -15
  25. data/lib/airbrake-ruby/promise.rb +1 -0
  26. data/lib/airbrake-ruby/query.rb +1 -6
  27. data/lib/airbrake-ruby/queue.rb +1 -8
  28. data/lib/airbrake-ruby/remote_settings.rb +145 -0
  29. data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
  30. data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
  31. data/lib/airbrake-ruby/request.rb +1 -8
  32. data/lib/airbrake-ruby/stat.rb +1 -12
  33. data/lib/airbrake-ruby/sync_sender.rb +3 -2
  34. data/lib/airbrake-ruby/tdigest.rb +2 -0
  35. data/lib/airbrake-ruby/thread_pool.rb +1 -0
  36. data/lib/airbrake-ruby/truncator.rb +8 -2
  37. data/lib/airbrake-ruby/version.rb +11 -1
  38. data/spec/airbrake_spec.rb +45 -22
  39. data/spec/backtrace_spec.rb +26 -26
  40. data/spec/code_hunk_spec.rb +2 -2
  41. data/spec/config/processor_spec.rb +125 -0
  42. data/spec/config/validator_spec.rb +18 -1
  43. data/spec/config_spec.rb +11 -32
  44. data/spec/filters/gem_root_filter_spec.rb +4 -4
  45. data/spec/filters/keys_allowlist_spec.rb +1 -0
  46. data/spec/filters/keys_blocklist_spec.rb +10 -0
  47. data/spec/filters/root_directory_filter_spec.rb +4 -4
  48. data/spec/filters/sql_filter_spec.rb +2 -2
  49. data/spec/notice_notifier/options_spec.rb +2 -2
  50. data/spec/notice_notifier_spec.rb +2 -2
  51. data/spec/notice_spec.rb +1 -1
  52. data/spec/performance_breakdown_spec.rb +0 -12
  53. data/spec/performance_notifier_spec.rb +0 -25
  54. data/spec/query_spec.rb +1 -11
  55. data/spec/queue_spec.rb +1 -13
  56. data/spec/remote_settings/callback_spec.rb +141 -0
  57. data/spec/remote_settings/settings_data_spec.rb +348 -0
  58. data/spec/remote_settings_spec.rb +230 -0
  59. data/spec/request_spec.rb +1 -13
  60. data/spec/spec_helper.rb +4 -4
  61. data/spec/stat_spec.rb +0 -9
  62. data/spec/sync_sender_spec.rb +3 -1
  63. metadata +18 -6
@@ -151,6 +151,7 @@ RSpec.describe Airbrake::Filters::KeysAllowlist 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
@@ -150,6 +150,16 @@ RSpec.describe Airbrake::Filters::KeysBlocklist 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 Metrics/LineLength
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 Metrics/LineLength
15
+ # rubocop:enable Layout/LineLength
16
16
 
17
17
  subject.call(notice)
18
18
 
19
- # rubocop:disable Metrics/LineLength
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 Metrics/LineLength
30
+ # rubocop:enable Layout/LineLength
31
31
  end
32
32
 
33
33
  it "does not filter file when it is nil" do
@@ -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 Metrics/LineLength
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 Metrics/LineLength
232
+ # rubocop:enable Layout/LineLength
233
233
 
234
234
  [
235
235
  'COMMIT',
@@ -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 Metrics/LineLength
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 Metrics/LineLength
155
+ # rubocop:enable Layout/LineLength
156
156
  end
157
157
  end
158
158
 
@@ -309,7 +309,7 @@ RSpec.describe Airbrake::NoticeNotifier do
309
309
 
310
310
  notice = subject.build_notice(Exception.new)
311
311
 
312
- # rubocop:disable Metrics/LineLength
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 Metrics/LineLength
320
+ # rubocop:enable Layout/LineLength
321
321
  end
322
322
  end
323
323
 
@@ -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 equal(hash)
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)
@@ -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: '', start_time: Time.now,
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
@@ -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,141 @@
1
+ RSpec.describe Airbrake::RemoteSettings::Callback do
2
+ describe "#call" do
3
+ let(:logger) { Logger.new(File::NULL) }
4
+
5
+ let(:config) do
6
+ Airbrake::Config.new(
7
+ project_id: 123,
8
+ logger: logger,
9
+ )
10
+ end
11
+
12
+ let(:data) do
13
+ instance_double(Airbrake::RemoteSettings::SettingsData)
14
+ end
15
+
16
+ before do
17
+ allow(data).to receive(:to_h)
18
+ allow(data).to receive(:error_host)
19
+ allow(data).to receive(:apm_host)
20
+ allow(data).to receive(:error_notifications?)
21
+ allow(data).to receive(:performance_stats?)
22
+ end
23
+
24
+ it "logs given data" do
25
+ expect(logger).to receive(:debug).with(/applying remote settings/)
26
+ described_class.new(config).call(data)
27
+ end
28
+
29
+ context "when the config disables error notifications" do
30
+ before do
31
+ config.error_notifications = false
32
+ allow(data).to receive(:error_notifications?).and_return(true)
33
+ end
34
+
35
+ it "keeps the option disabled forever" do
36
+ callback = described_class.new(config)
37
+
38
+ callback.call(data)
39
+ expect(config.error_notifications).to eq(false)
40
+
41
+ callback.call(data)
42
+ expect(config.error_notifications).to eq(false)
43
+
44
+ callback.call(data)
45
+ expect(config.error_notifications).to eq(false)
46
+ end
47
+ end
48
+
49
+ context "when the config enables error notifications" do
50
+ before { config.error_notifications = true }
51
+
52
+ it "can disable and enable error notifications" do
53
+ expect(data).to receive(:error_notifications?).and_return(false)
54
+
55
+ callback = described_class.new(config)
56
+ callback.call(data)
57
+ expect(config.error_notifications).to eq(false)
58
+
59
+ expect(data).to receive(:error_notifications?).and_return(true)
60
+ callback.call(data)
61
+ expect(config.error_notifications).to eq(true)
62
+ end
63
+ end
64
+
65
+ context "when the config disables performance_stats" do
66
+ before do
67
+ config.performance_stats = false
68
+ allow(data).to receive(:performance_stats?).and_return(true)
69
+ end
70
+
71
+ it "keeps the option disabled forever" do
72
+ callback = described_class.new(config)
73
+
74
+ callback.call(data)
75
+ expect(config.performance_stats).to eq(false)
76
+
77
+ callback.call(data)
78
+ expect(config.performance_stats).to eq(false)
79
+
80
+ callback.call(data)
81
+ expect(config.performance_stats).to eq(false)
82
+ end
83
+ end
84
+
85
+ context "when the config enables performance stats" do
86
+ before { config.performance_stats = true }
87
+
88
+ it "can disable and enable performance_stats" do
89
+ expect(data).to receive(:performance_stats?).and_return(false)
90
+
91
+ callback = described_class.new(config)
92
+ callback.call(data)
93
+ expect(config.performance_stats).to eq(false)
94
+
95
+ expect(data).to receive(:performance_stats?).and_return(true)
96
+ callback.call(data)
97
+ expect(config.performance_stats).to eq(true)
98
+ end
99
+ end
100
+
101
+ context "when error_host returns a value" do
102
+ it "sets the error_host option" do
103
+ config.error_host = 'http://api.airbrake.io'
104
+ allow(data).to receive(:error_host).and_return('https://api.example.com')
105
+
106
+ described_class.new(config).call(data)
107
+ expect(config.error_host).to eq('https://api.example.com')
108
+ end
109
+ end
110
+
111
+ context "when error_host returns nil" do
112
+ it "doesn't modify the error_host option" do
113
+ config.error_host = 'http://api.airbrake.io'
114
+ allow(data).to receive(:error_host).and_return(nil)
115
+
116
+ described_class.new(config).call(data)
117
+ expect(config.error_host).to eq('http://api.airbrake.io')
118
+ end
119
+ end
120
+
121
+ context "when apm_host returns a value" do
122
+ it "sets the apm_host option" do
123
+ config.apm_host = 'http://api.airbrake.io'
124
+ allow(data).to receive(:apm_host).and_return('https://api.example.com')
125
+
126
+ described_class.new(config).call(data)
127
+ expect(config.apm_host).to eq('https://api.example.com')
128
+ end
129
+ end
130
+
131
+ context "when apm_host returns nil" do
132
+ it "doesn't modify the apm_host option" do
133
+ config.apm_host = 'http://api.airbrake.io'
134
+ allow(data).to receive(:apm_host).and_return(nil)
135
+
136
+ described_class.new(config).call(data)
137
+ expect(config.apm_host).to eq('http://api.airbrake.io')
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,348 @@
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')
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) { 'http://example.com/' }
64
+
65
+ context "when remote config specifies a config route" do
66
+ let(:data) do
67
+ { 'config_route' => '123/cfg/321/cfg.json' }
68
+ end
69
+
70
+ it "returns the config route with the provided location" do
71
+ expect(described_class.new(project_id, data).config_route(host)).to eq(
72
+ 'http://example.com/123/cfg/321/cfg.json',
73
+ )
74
+ end
75
+ end
76
+
77
+ context "when remote config DOES NOT specify a config route" do
78
+ it "returns the config route with the default location" do
79
+ expect(described_class.new(project_id, {}).config_route(host)).to eq(
80
+ "http://example.com/2020-06-18/config/#{project_id}/config.json",
81
+ )
82
+ end
83
+ end
84
+
85
+ context "when a config route is specified but is set to nil" do
86
+ let(:data) do
87
+ { 'config_route' => nil }
88
+ end
89
+
90
+ it "returns the config route with the default location" do
91
+ expect(described_class.new(project_id, data).config_route(host)).to eq(
92
+ "http://example.com/2020-06-18/config/#{project_id}/config.json",
93
+ )
94
+ end
95
+ end
96
+
97
+ context "when a config route is specified but is set to an empty string" do
98
+ let(:data) do
99
+ { 'config_route' => '' }
100
+ end
101
+
102
+ it "returns the route with the default instead" do
103
+ expect(described_class.new(project_id, data).config_route(host)).to eq(
104
+ "http://example.com/2020-06-18/config/#{project_id}/config.json",
105
+ )
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "#error_notifications?" do
111
+ context "when the 'errors' setting is present" do
112
+ context "and when it is enabled" do
113
+ let(:data) do
114
+ {
115
+ 'settings' => [
116
+ {
117
+ 'name' => 'errors',
118
+ 'enabled' => true,
119
+ },
120
+ ],
121
+ }
122
+ end
123
+
124
+ it "returns true" do
125
+ expect(described_class.new(project_id, data).error_notifications?)
126
+ .to eq(true)
127
+ end
128
+ end
129
+
130
+ context "and when it is disabled" do
131
+ let(:data) do
132
+ {
133
+ 'settings' => [
134
+ {
135
+ 'name' => 'errors',
136
+ 'enabled' => false,
137
+ },
138
+ ],
139
+ }
140
+ end
141
+
142
+ it "returns false" do
143
+ expect(described_class.new(project_id, data).error_notifications?)
144
+ .to eq(false)
145
+ end
146
+ end
147
+ end
148
+
149
+ context "when the 'errors' setting is missing" do
150
+ let(:data) do
151
+ { 'settings' => [] }
152
+ end
153
+
154
+ it "returns true" do
155
+ expect(described_class.new(project_id, data).error_notifications?)
156
+ .to eq(true)
157
+ end
158
+ end
159
+ end
160
+
161
+ describe "#performance_stats?" do
162
+ context "when the 'apm' setting is present" do
163
+ context "and when it is enabled" do
164
+ let(:data) do
165
+ {
166
+ 'settings' => [
167
+ {
168
+ 'name' => 'apm',
169
+ 'enabled' => true,
170
+ },
171
+ ],
172
+ }
173
+ end
174
+
175
+ it "returns true" do
176
+ expect(described_class.new(project_id, data).performance_stats?)
177
+ .to eq(true)
178
+ end
179
+ end
180
+
181
+ context "and when it is disabled" do
182
+ let(:data) do
183
+ {
184
+ 'settings' => [
185
+ {
186
+ 'name' => 'apm',
187
+ 'enabled' => false,
188
+ },
189
+ ],
190
+ }
191
+ end
192
+
193
+ it "returns false" do
194
+ expect(described_class.new(project_id, data).performance_stats?)
195
+ .to eq(false)
196
+ end
197
+ end
198
+ end
199
+
200
+ context "when the 'apm' setting is missing" do
201
+ let(:data) do
202
+ { 'settings' => [] }
203
+ end
204
+
205
+ it "returns true" do
206
+ expect(described_class.new(project_id, data).performance_stats?)
207
+ .to eq(true)
208
+ end
209
+ end
210
+ end
211
+
212
+ describe "#error_host" do
213
+ context "when the 'errors' setting is present" do
214
+ context "and when 'endpoint' is specified" do
215
+ let(:endpoint) { 'https://api.example.com/' }
216
+
217
+ let(:data) do
218
+ {
219
+ 'settings' => [
220
+ {
221
+ 'name' => 'errors',
222
+ 'enabled' => true,
223
+ 'endpoint' => endpoint,
224
+ },
225
+ ],
226
+ }
227
+ end
228
+
229
+ it "returns the endpoint" do
230
+ expect(described_class.new(project_id, data).error_host).to eq(endpoint)
231
+ end
232
+ end
233
+
234
+ context "and when an endpoint is NOT specified" do
235
+ let(:data) do
236
+ {
237
+ 'settings' => [
238
+ {
239
+ 'name' => 'errors',
240
+ 'enabled' => true,
241
+ },
242
+ ],
243
+ }
244
+ end
245
+
246
+ it "returns nil" do
247
+ expect(described_class.new(project_id, data).error_host).to be_nil
248
+ end
249
+ end
250
+ end
251
+
252
+ context "when the 'errors' setting is missing" do
253
+ let(:data) do
254
+ { 'settings' => [] }
255
+ end
256
+
257
+ it "returns nil" do
258
+ expect(described_class.new(project_id, data).error_host).to be_nil
259
+ end
260
+ end
261
+ end
262
+
263
+ describe "#apm_host" do
264
+ context "when the 'apm' setting is present" do
265
+ context "and when 'endpoint' is specified" do
266
+ let(:endpoint) { 'https://api.example.com/' }
267
+
268
+ let(:data) do
269
+ {
270
+ 'settings' => [
271
+ {
272
+ 'name' => 'apm',
273
+ 'enabled' => true,
274
+ 'endpoint' => endpoint,
275
+ },
276
+ ],
277
+ }
278
+ end
279
+
280
+ it "returns the endpoint" do
281
+ expect(described_class.new(project_id, data).apm_host).to eq(endpoint)
282
+ end
283
+ end
284
+
285
+ context "and when an endpoint is NOT specified" do
286
+ let(:data) do
287
+ {
288
+ 'settings' => [
289
+ {
290
+ 'name' => 'apm',
291
+ 'enabled' => true,
292
+ },
293
+ ],
294
+ }
295
+ end
296
+
297
+ it "returns nil" do
298
+ expect(described_class.new(project_id, data).apm_host).to be_nil
299
+ end
300
+ end
301
+ end
302
+
303
+ context "when the 'apm' setting is missing" do
304
+ let(:data) do
305
+ { 'settings' => [] }
306
+ end
307
+
308
+ it "returns nil" do
309
+ expect(described_class.new(project_id, data).apm_host).to be_nil
310
+ end
311
+ end
312
+ end
313
+
314
+ describe "#to_h" do
315
+ let(:data) do
316
+ {
317
+ 'poll_sec' => 123,
318
+ 'settings' => [
319
+ {
320
+ 'name' => 'apm',
321
+ 'enabled' => false,
322
+ },
323
+ ],
324
+ }
325
+ end
326
+
327
+ subject { described_class.new(project_id, data) }
328
+
329
+ it "returns a hash representation of settings" do
330
+ expect(described_class.new(project_id, data).to_h).to eq(data)
331
+ end
332
+
333
+ it "doesn't allow mutation of the original data object" do
334
+ hash = subject.to_h
335
+ hash['poll_sec'] = 0
336
+
337
+ expect(subject.to_h).to eq(
338
+ 'poll_sec' => 123,
339
+ 'settings' => [
340
+ {
341
+ 'name' => 'apm',
342
+ 'enabled' => false,
343
+ },
344
+ ],
345
+ )
346
+ end
347
+ end
348
+ end