airbrake-ruby 4.15.0 → 6.1.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.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/lib/airbrake-ruby/async_sender.rb +4 -2
  3. data/lib/airbrake-ruby/backtrace.rb +6 -5
  4. data/lib/airbrake-ruby/config/processor.rb +77 -0
  5. data/lib/airbrake-ruby/config/validator.rb +6 -0
  6. data/lib/airbrake-ruby/config.rb +44 -35
  7. data/lib/airbrake-ruby/context.rb +51 -0
  8. data/lib/airbrake-ruby/file_cache.rb +1 -1
  9. data/lib/airbrake-ruby/filter_chain.rb +3 -0
  10. data/lib/airbrake-ruby/filters/context_filter.rb +4 -5
  11. data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
  12. data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +1 -1
  13. data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
  14. data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +3 -4
  15. data/lib/airbrake-ruby/filters/git_repository_filter.rb +11 -2
  16. data/lib/airbrake-ruby/filters/git_revision_filter.rb +3 -1
  17. data/lib/airbrake-ruby/filters/keys_filter.rb +23 -15
  18. data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
  19. data/lib/airbrake-ruby/filters/sql_filter.rb +11 -11
  20. data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
  21. data/lib/airbrake-ruby/filters/thread_filter.rb +4 -3
  22. data/lib/airbrake-ruby/grouppable.rb +1 -1
  23. data/lib/airbrake-ruby/ignorable.rb +1 -2
  24. data/lib/airbrake-ruby/mergeable.rb +1 -1
  25. data/lib/airbrake-ruby/monotonic_time.rb +1 -1
  26. data/lib/airbrake-ruby/notice.rb +1 -8
  27. data/lib/airbrake-ruby/notice_notifier.rb +4 -4
  28. data/lib/airbrake-ruby/performance_breakdown.rb +1 -6
  29. data/lib/airbrake-ruby/performance_notifier.rb +40 -54
  30. data/lib/airbrake-ruby/promise.rb +1 -0
  31. data/lib/airbrake-ruby/query.rb +1 -6
  32. data/lib/airbrake-ruby/queue.rb +1 -8
  33. data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
  34. data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
  35. data/lib/airbrake-ruby/remote_settings.rb +128 -0
  36. data/lib/airbrake-ruby/request.rb +1 -8
  37. data/lib/airbrake-ruby/stat.rb +2 -13
  38. data/lib/airbrake-ruby/sync_sender.rb +3 -2
  39. data/lib/airbrake-ruby/tdigest.rb +12 -9
  40. data/lib/airbrake-ruby/thread_pool.rb +9 -6
  41. data/lib/airbrake-ruby/time_truncate.rb +2 -2
  42. data/lib/airbrake-ruby/timed_trace.rb +1 -3
  43. data/lib/airbrake-ruby/truncator.rb +8 -2
  44. data/lib/airbrake-ruby/version.rb +11 -1
  45. data/lib/airbrake-ruby.rb +44 -54
  46. data/spec/airbrake_spec.rb +178 -92
  47. data/spec/async_sender_spec.rb +10 -8
  48. data/spec/backtrace_spec.rb +39 -36
  49. data/spec/benchmark_spec.rb +7 -5
  50. data/spec/code_hunk_spec.rb +26 -17
  51. data/spec/config/processor_spec.rb +167 -0
  52. data/spec/config/validator_spec.rb +23 -3
  53. data/spec/config_spec.rb +43 -55
  54. data/spec/context_spec.rb +54 -0
  55. data/spec/deploy_notifier_spec.rb +6 -4
  56. data/spec/file_cache_spec.rb +1 -0
  57. data/spec/filter_chain_spec.rb +29 -24
  58. data/spec/filters/context_filter_spec.rb +14 -5
  59. data/spec/filters/dependency_filter_spec.rb +3 -1
  60. data/spec/filters/exception_attributes_filter_spec.rb +5 -3
  61. data/spec/filters/gem_root_filter_spec.rb +9 -6
  62. data/spec/filters/git_last_checkout_filter_spec.rb +10 -12
  63. data/spec/filters/{git_repository_filter.rb → git_repository_filter_spec.rb} +26 -15
  64. data/spec/filters/git_revision_filter_spec.rb +20 -20
  65. data/spec/filters/keys_allowlist_spec.rb +26 -16
  66. data/spec/filters/keys_blocklist_spec.rb +35 -18
  67. data/spec/filters/root_directory_filter_spec.rb +7 -7
  68. data/spec/filters/sql_filter_spec.rb +28 -28
  69. data/spec/filters/system_exit_filter_spec.rb +4 -2
  70. data/spec/filters/thread_filter_spec.rb +16 -14
  71. data/spec/loggable_spec.rb +2 -2
  72. data/spec/monotonic_time_spec.rb +8 -6
  73. data/spec/nested_exception_spec.rb +46 -46
  74. data/spec/notice_notifier/options_spec.rb +25 -15
  75. data/spec/notice_notifier_spec.rb +54 -49
  76. data/spec/notice_spec.rb +7 -3
  77. data/spec/performance_breakdown_spec.rb +0 -12
  78. data/spec/performance_notifier_spec.rb +69 -87
  79. data/spec/promise_spec.rb +38 -32
  80. data/spec/query_spec.rb +1 -11
  81. data/spec/queue_spec.rb +1 -13
  82. data/spec/remote_settings/callback_spec.rb +162 -0
  83. data/spec/remote_settings/settings_data_spec.rb +348 -0
  84. data/spec/remote_settings_spec.rb +201 -0
  85. data/spec/request_spec.rb +1 -13
  86. data/spec/response_spec.rb +34 -12
  87. data/spec/spec_helper.rb +4 -4
  88. data/spec/stashable_spec.rb +5 -5
  89. data/spec/stat_spec.rb +7 -14
  90. data/spec/sync_sender_spec.rb +52 -17
  91. data/spec/tdigest_spec.rb +61 -56
  92. data/spec/thread_pool_spec.rb +67 -58
  93. data/spec/time_truncate_spec.rb +23 -6
  94. data/spec/timed_trace_spec.rb +32 -30
  95. data/spec/truncator_spec.rb +72 -43
  96. metadata +67 -51
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
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: '', 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
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,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 be(false)
46
+
47
+ callback.call(data)
48
+ expect(config.error_notifications).to be(false)
49
+
50
+ callback.call(data)
51
+ expect(config.error_notifications).to be(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 be(false)
67
+
68
+ allow(data).to receive(:error_notifications?).and_return(true)
69
+
70
+ callback.call(data)
71
+ expect(config.error_notifications).to be(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 be(false)
90
+
91
+ callback.call(data)
92
+ expect(config.performance_stats).to be(false)
93
+
94
+ callback.call(data)
95
+ expect(config.performance_stats).to be(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 be(false)
111
+
112
+ allow(data).to receive(:performance_stats?).and_return(true)
113
+
114
+ callback.call(data)
115
+ expect(config.performance_stats).to be(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