airbrake-ruby 5.0.1-java → 5.2.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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby.rb +4 -2
  3. data/lib/airbrake-ruby/async_sender.rb +3 -1
  4. data/lib/airbrake-ruby/config.rb +15 -6
  5. data/lib/airbrake-ruby/config/processor.rb +7 -20
  6. data/lib/airbrake-ruby/context.rb +51 -0
  7. data/lib/airbrake-ruby/file_cache.rb +1 -1
  8. data/lib/airbrake-ruby/filter_chain.rb +2 -0
  9. data/lib/airbrake-ruby/filters/context_filter.rb +4 -5
  10. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +1 -1
  11. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +1 -1
  12. data/lib/airbrake-ruby/filters/git_revision_filter.rb +1 -1
  13. data/lib/airbrake-ruby/filters/keys_filter.rb +2 -2
  14. data/lib/airbrake-ruby/filters/sql_filter.rb +2 -2
  15. data/lib/airbrake-ruby/filters/thread_filter.rb +2 -3
  16. data/lib/airbrake-ruby/grouppable.rb +1 -1
  17. data/lib/airbrake-ruby/ignorable.rb +0 -2
  18. data/lib/airbrake-ruby/mergeable.rb +1 -1
  19. data/lib/airbrake-ruby/notice_notifier.rb +3 -4
  20. data/lib/airbrake-ruby/performance_notifier.rb +1 -2
  21. data/lib/airbrake-ruby/remote_settings.rb +10 -50
  22. data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
  23. data/lib/airbrake-ruby/remote_settings/settings_data.rb +3 -8
  24. data/lib/airbrake-ruby/tdigest.rb +7 -6
  25. data/lib/airbrake-ruby/thread_pool.rb +5 -3
  26. data/lib/airbrake-ruby/timed_trace.rb +1 -3
  27. data/lib/airbrake-ruby/version.rb +2 -2
  28. data/spec/airbrake_spec.rb +139 -76
  29. data/spec/async_sender_spec.rb +10 -8
  30. data/spec/backtrace_spec.rb +13 -10
  31. data/spec/benchmark_spec.rb +5 -3
  32. data/spec/code_hunk_spec.rb +24 -15
  33. data/spec/config/processor_spec.rb +29 -87
  34. data/spec/config/validator_spec.rb +5 -2
  35. data/spec/config_spec.rb +26 -17
  36. data/spec/context_spec.rb +54 -0
  37. data/spec/deploy_notifier_spec.rb +6 -4
  38. data/spec/file_cache_spec.rb +1 -0
  39. data/spec/filter_chain_spec.rb +29 -24
  40. data/spec/filters/context_filter_spec.rb +14 -5
  41. data/spec/filters/dependency_filter_spec.rb +3 -1
  42. data/spec/filters/exception_attributes_filter_spec.rb +5 -3
  43. data/spec/filters/gem_root_filter_spec.rb +5 -2
  44. data/spec/filters/git_last_checkout_filter_spec.rb +10 -12
  45. data/spec/filters/git_repository_filter.rb +9 -9
  46. data/spec/filters/git_revision_filter_spec.rb +19 -19
  47. data/spec/filters/keys_allowlist_spec.rb +25 -16
  48. data/spec/filters/keys_blocklist_spec.rb +25 -18
  49. data/spec/filters/root_directory_filter_spec.rb +3 -3
  50. data/spec/filters/sql_filter_spec.rb +26 -26
  51. data/spec/filters/system_exit_filter_spec.rb +4 -2
  52. data/spec/filters/thread_filter_spec.rb +16 -14
  53. data/spec/loggable_spec.rb +2 -2
  54. data/spec/monotonic_time_spec.rb +8 -6
  55. data/spec/nested_exception_spec.rb +46 -46
  56. data/spec/notice_notifier/options_spec.rb +23 -13
  57. data/spec/notice_notifier_spec.rb +52 -47
  58. data/spec/notice_spec.rb +6 -2
  59. data/spec/performance_notifier_spec.rb +67 -60
  60. data/spec/promise_spec.rb +38 -32
  61. data/spec/remote_settings/callback_spec.rb +162 -0
  62. data/spec/remote_settings/settings_data_spec.rb +23 -53
  63. data/spec/remote_settings_spec.rb +42 -75
  64. data/spec/response_spec.rb +34 -12
  65. data/spec/stashable_spec.rb +5 -5
  66. data/spec/stat_spec.rb +7 -5
  67. data/spec/sync_sender_spec.rb +49 -16
  68. data/spec/tdigest_spec.rb +61 -56
  69. data/spec/thread_pool_spec.rb +65 -55
  70. data/spec/time_truncate_spec.rb +4 -2
  71. data/spec/timed_trace_spec.rb +32 -30
  72. data/spec/truncator_spec.rb +72 -43
  73. metadata +53 -47
data/spec/promise_spec.rb CHANGED
@@ -1,15 +1,17 @@
1
1
  RSpec.describe Airbrake::Promise do
2
+ subject(:promise) { described_class.new }
3
+
2
4
  describe ".then" do
3
5
  let(:resolved_with) { [] }
4
6
  let(:rejected_with) { [] }
5
7
 
6
8
  context "when it is not resolved" do
7
9
  it "returns self" do
8
- expect(subject.then {}).to eq(subject)
10
+ expect(promise.then { anything }).to eq(promise)
9
11
  end
10
12
 
11
13
  it "doesn't call the resolve callbacks yet" do
12
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
14
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
13
15
  expect(resolved_with).to be_empty
14
16
  end
15
17
  end
@@ -17,12 +19,12 @@ RSpec.describe Airbrake::Promise do
17
19
  context "when it is resolved" do
18
20
  shared_examples "then specs" do
19
21
  it "returns self" do
20
- expect(subject.then {}).to eq(subject)
22
+ expect(promise.then { anything }).to eq(promise)
21
23
  end
22
24
 
23
25
  it "yields the resolved value" do
24
26
  yielded = nil
25
- subject.then { |value| yielded = value }
27
+ promise.then { |value| yielded = value }
26
28
  expect(yielded).to eq('id' => '123')
27
29
  end
28
30
 
@@ -37,29 +39,29 @@ RSpec.describe Airbrake::Promise do
37
39
 
38
40
  context "and there are some resolve and reject callbacks in place" do
39
41
  before do
40
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
41
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
42
- subject.resolve('id' => '123')
42
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
43
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
44
+ promise.resolve('id' => '123')
43
45
  end
44
46
 
45
47
  include_examples "then specs"
46
48
 
47
49
  it "registers the resolve callbacks" do
48
- subject.resolve('id' => '456')
50
+ promise.resolve('id' => '456')
49
51
  expect(resolved_with).to match_array([1, 2, 1, 2])
50
52
  end
51
53
  end
52
54
 
53
55
  context "and additional then callbacks are added" do
54
56
  before do
55
- subject.resolve('id' => '123')
56
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
57
+ promise.resolve('id' => '123')
58
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
57
59
  end
58
60
 
59
61
  include_examples "then specs"
60
62
 
61
63
  it "doesn't register new resolve callbacks" do
62
- subject.resolve('id' => '456')
64
+ promise.resolve('id' => '456')
63
65
  expect(resolved_with).to match_array([1, 2])
64
66
  end
65
67
  end
@@ -72,11 +74,11 @@ RSpec.describe Airbrake::Promise do
72
74
 
73
75
  context "when it is not rejected" do
74
76
  it "returns self" do
75
- expect(subject.then {}).to eq(subject)
77
+ expect(promise.then { anything }).to eq(promise)
76
78
  end
77
79
 
78
80
  it "doesn't call the reject callbacks yet" do
79
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
81
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
80
82
  expect(rejected_with).to be_empty
81
83
  end
82
84
  end
@@ -84,12 +86,12 @@ RSpec.describe Airbrake::Promise do
84
86
  context "when it is rejected" do
85
87
  shared_examples "rescue specs" do
86
88
  it "returns self" do
87
- expect(subject.rescue {}).to eq(subject)
89
+ expect(promise.rescue { anything }).to eq(promise)
88
90
  end
89
91
 
90
92
  it "yields the rejected value" do
91
93
  yielded = nil
92
- subject.rescue { |value| yielded = value }
94
+ promise.rescue { |value| yielded = value }
93
95
  expect(yielded).to eq('bingo')
94
96
  end
95
97
 
@@ -104,29 +106,29 @@ RSpec.describe Airbrake::Promise do
104
106
 
105
107
  context "and there are some resolve and reject callbacks in place" do
106
108
  before do
107
- subject.then { resolved_with << 1 }.then { resolved_with << 2 }
108
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
109
- subject.reject('bingo')
109
+ promise.then { resolved_with << 1 }.then { resolved_with << 2 }
110
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
111
+ promise.reject('bingo')
110
112
  end
111
113
 
112
114
  include_examples "rescue specs"
113
115
 
114
116
  it "registers the reject callbacks" do
115
- subject.reject('bingo again')
117
+ promise.reject('bingo again')
116
118
  expect(rejected_with).to match_array([1, 2, 1, 2])
117
119
  end
118
120
  end
119
121
 
120
122
  context "and additional reject callbacks are added" do
121
123
  before do
122
- subject.reject('bingo')
123
- subject.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
124
+ promise.reject('bingo')
125
+ promise.rescue { rejected_with << 1 }.rescue { rejected_with << 2 }
124
126
  end
125
127
 
126
128
  include_examples "rescue specs"
127
129
 
128
130
  it "doesn't register new reject callbacks" do
129
- subject.reject('bingo again')
131
+ promise.reject('bingo again')
130
132
  expect(rejected_with).to match_array([1, 2])
131
133
  end
132
134
  end
@@ -135,37 +137,38 @@ RSpec.describe Airbrake::Promise do
135
137
 
136
138
  describe ".resolve" do
137
139
  it "returns self" do
138
- expect(subject.resolve(1)).to eq(subject)
140
+ expect(promise.resolve(1)).to eq(promise)
139
141
  end
140
142
 
141
143
  it "executes callbacks attached with .then" do
142
144
  array = []
143
- subject.then { |notice_id| array << notice_id }.rescue { array << 999 }
145
+ promise.then { |notice_id| array << notice_id }.rescue { array << 999 }
144
146
 
145
147
  expect(array.size).to be_zero
146
- subject.resolve(1)
148
+ promise.resolve(1)
147
149
  expect(array).to match_array([1])
148
150
  end
149
151
  end
150
152
 
151
153
  describe ".reject" do
152
154
  it "returns self" do
153
- expect(subject.reject(1)).to eq(subject)
155
+ expect(promise.reject(1)).to eq(promise)
154
156
  end
155
157
 
156
158
  it "executes callbacks attached with .rescue" do
157
159
  array = []
158
- subject.then { array << 1 }.rescue { |error| array << error }
160
+ promise.then { array << 1 }.rescue { |error| array << error }
159
161
 
160
162
  expect(array.size).to be_zero
161
- subject.reject(999)
163
+ promise.reject(999)
162
164
  expect(array).to match_array([999])
163
165
  end
164
166
  end
165
167
 
166
168
  describe "#rejected?" do
167
169
  context "when it was rejected" do
168
- before { subject.reject(1) }
170
+ before { promise.reject(1) }
171
+
169
172
  it { is_expected.to be_rejected }
170
173
  end
171
174
 
@@ -174,14 +177,16 @@ RSpec.describe Airbrake::Promise do
174
177
  end
175
178
 
176
179
  context "when it was resolved" do
177
- before { subject.resolve }
180
+ before { promise.resolve }
181
+
178
182
  it { is_expected.not_to be_rejected }
179
183
  end
180
184
  end
181
185
 
182
186
  describe "#resolved?" do
183
187
  context "when it was resolved" do
184
- before { subject.resolve }
188
+ before { promise.resolve }
189
+
185
190
  it { is_expected.to be_resolved }
186
191
  end
187
192
 
@@ -190,7 +195,8 @@ RSpec.describe Airbrake::Promise do
190
195
  end
191
196
 
192
197
  context "when it was rejected" do
193
- before { subject.reject(1) }
198
+ before { promise.reject(1) }
199
+
194
200
  it { is_expected.not_to be_resolved }
195
201
  end
196
202
  end
@@ -0,0 +1,162 @@
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
+ allow(logger).to receive(:debug)
26
+
27
+ described_class.new(config).call(data)
28
+
29
+ expect(logger).to have_received(:debug) do |&block|
30
+ expect(block.call).to match(/applying remote settings/)
31
+ end
32
+ end
33
+
34
+ context "when the config disables error notifications" do
35
+ before do
36
+ config.error_notifications = false
37
+ allow(data).to receive(:error_notifications?).and_return(true)
38
+ end
39
+
40
+ # rubocop:disable RSpec/MultipleExpectations
41
+ it "keeps the option disabled forever" do
42
+ callback = described_class.new(config)
43
+
44
+ callback.call(data)
45
+ expect(config.error_notifications).to eq(false)
46
+
47
+ callback.call(data)
48
+ expect(config.error_notifications).to eq(false)
49
+
50
+ callback.call(data)
51
+ expect(config.error_notifications).to eq(false)
52
+ end
53
+ # rubocop:enable RSpec/MultipleExpectations
54
+ end
55
+
56
+ context "when the config enables error notifications" do
57
+ before { config.error_notifications = true }
58
+
59
+ # rubocop:disable RSpec/MultipleExpectations
60
+ it "can disable and enable error notifications" do
61
+ callback = described_class.new(config)
62
+
63
+ allow(data).to receive(:error_notifications?).and_return(false)
64
+
65
+ callback.call(data)
66
+ expect(config.error_notifications).to eq(false)
67
+
68
+ allow(data).to receive(:error_notifications?).and_return(true)
69
+
70
+ callback.call(data)
71
+ expect(config.error_notifications).to eq(true)
72
+
73
+ expect(data).to have_received(:error_notifications?).twice
74
+ end
75
+ # rubocop:enable RSpec/MultipleExpectations
76
+ end
77
+
78
+ context "when the config disables performance_stats" do
79
+ before do
80
+ config.performance_stats = false
81
+ allow(data).to receive(:performance_stats?).and_return(true)
82
+ end
83
+
84
+ # rubocop:disable RSpec/MultipleExpectations
85
+ it "keeps the option disabled forever" do
86
+ callback = described_class.new(config)
87
+
88
+ callback.call(data)
89
+ expect(config.performance_stats).to eq(false)
90
+
91
+ callback.call(data)
92
+ expect(config.performance_stats).to eq(false)
93
+
94
+ callback.call(data)
95
+ expect(config.performance_stats).to eq(false)
96
+ end
97
+ # rubocop:enable RSpec/MultipleExpectations
98
+ end
99
+
100
+ context "when the config enables performance stats" do
101
+ before { config.performance_stats = true }
102
+
103
+ # rubocop:disable RSpec/MultipleExpectations
104
+ it "can disable and enable performance_stats" do
105
+ callback = described_class.new(config)
106
+
107
+ allow(data).to receive(:performance_stats?).and_return(false)
108
+
109
+ callback.call(data)
110
+ expect(config.performance_stats).to eq(false)
111
+
112
+ allow(data).to receive(:performance_stats?).and_return(true)
113
+
114
+ callback.call(data)
115
+ expect(config.performance_stats).to eq(true)
116
+
117
+ expect(data).to have_received(:performance_stats?).twice
118
+ end
119
+ # rubocop:enable RSpec/MultipleExpectations
120
+ end
121
+
122
+ context "when error_host returns a value" do
123
+ it "sets the error_host option" do
124
+ config.error_host = 'http://api.airbrake.io'
125
+ allow(data).to receive(:error_host).and_return('https://api.example.com')
126
+
127
+ described_class.new(config).call(data)
128
+ expect(config.error_host).to eq('https://api.example.com')
129
+ end
130
+ end
131
+
132
+ context "when error_host returns nil" do
133
+ it "doesn't modify the error_host option" do
134
+ config.error_host = 'http://api.airbrake.io'
135
+ allow(data).to receive(:error_host).and_return(nil)
136
+
137
+ described_class.new(config).call(data)
138
+ expect(config.error_host).to eq('http://api.airbrake.io')
139
+ end
140
+ end
141
+
142
+ context "when apm_host returns a value" do
143
+ it "sets the apm_host option" do
144
+ config.apm_host = 'http://api.airbrake.io'
145
+ allow(data).to receive(:apm_host).and_return('https://api.example.com')
146
+
147
+ described_class.new(config).call(data)
148
+ expect(config.apm_host).to eq('https://api.example.com')
149
+ end
150
+ end
151
+
152
+ context "when apm_host returns nil" do
153
+ it "doesn't modify the apm_host option" do
154
+ config.apm_host = 'http://api.airbrake.io'
155
+ allow(data).to receive(:apm_host).and_return(nil)
156
+
157
+ described_class.new(config).call(data)
158
+ expect(config.apm_host).to eq('http://api.airbrake.io')
159
+ end
160
+ end
161
+ end
162
+ end
@@ -13,7 +13,7 @@ RSpec.describe Airbrake::RemoteSettings::SettingsData do
13
13
 
14
14
  expect(settings_data.interval).to eq(123)
15
15
  expect(settings_data.config_route(''))
16
- .to eq('abc/2020-06-18/config/123/config.json')
16
+ .to eq('/abc')
17
17
  end
18
18
  end
19
19
 
@@ -60,78 +60,48 @@ RSpec.describe Airbrake::RemoteSettings::SettingsData do
60
60
  end
61
61
 
62
62
  describe "#config_route" do
63
- let(:host) { 'https://v1-production-notifier-configs.s3.amazonaws.com' }
63
+ let(:host) { 'http://example.com/' }
64
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
65
+ context "when remote config specifies a config route" do
66
+ let(:data) do
67
+ { 'config_route' => '123/cfg/321/cfg.json' }
76
68
  end
77
69
 
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
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
+ )
88
74
  end
89
75
  end
90
76
 
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 given 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
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
+ )
110
82
  end
111
83
  end
112
84
 
113
- context "when the given remote host in the remote config is nil" do
85
+ context "when a config route is specified but is set to nil" do
114
86
  let(:data) do
115
87
  { 'config_route' => nil }
116
88
  end
117
89
 
118
- it "returns the route with the given host instead" do
90
+ it "returns the config route with the default location" do
119
91
  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",
92
+ "http://example.com/2020-06-18/config/#{project_id}/config.json",
122
93
  )
123
94
  end
124
95
  end
125
96
 
126
- context "when the given remote host in the remote config is an empty string" do
97
+ context "when a config route is specified but is set to an empty string" do
127
98
  let(:data) do
128
99
  { 'config_route' => '' }
129
100
  end
130
101
 
131
102
  it "returns the route with the default instead" do
132
103
  expect(described_class.new(project_id, data).config_route(host)).to eq(
133
- 'https://v1-production-notifier-configs.s3.amazonaws.com/' \
134
- "2020-06-18/config/#{project_id}/config.json",
104
+ "http://example.com/2020-06-18/config/#{project_id}/config.json",
135
105
  )
136
106
  end
137
107
  end
@@ -342,6 +312,8 @@ RSpec.describe Airbrake::RemoteSettings::SettingsData do
342
312
  end
343
313
 
344
314
  describe "#to_h" do
315
+ subject(:settings_data) { described_class.new(project_id, data) }
316
+
345
317
  let(:data) do
346
318
  {
347
319
  'poll_sec' => 123,
@@ -354,17 +326,15 @@ RSpec.describe Airbrake::RemoteSettings::SettingsData do
354
326
  }
355
327
  end
356
328
 
357
- subject { described_class.new(project_id, data) }
358
-
359
329
  it "returns a hash representation of settings" do
360
330
  expect(described_class.new(project_id, data).to_h).to eq(data)
361
331
  end
362
332
 
363
333
  it "doesn't allow mutation of the original data object" do
364
- hash = subject.to_h
334
+ hash = settings_data.to_h
365
335
  hash['poll_sec'] = 0
366
336
 
367
- expect(subject.to_h).to eq(
337
+ expect(settings_data.to_h).to eq(
368
338
  'poll_sec' => 123,
369
339
  'settings' => [
370
340
  {