airbrake-ruby 3.2.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.
- checksums.yaml +7 -0
- data/lib/airbrake-ruby.rb +554 -0
- data/lib/airbrake-ruby/async_sender.rb +119 -0
- data/lib/airbrake-ruby/backtrace.rb +194 -0
- data/lib/airbrake-ruby/code_hunk.rb +53 -0
- data/lib/airbrake-ruby/config.rb +238 -0
- data/lib/airbrake-ruby/config/validator.rb +63 -0
- data/lib/airbrake-ruby/deploy_notifier.rb +47 -0
- data/lib/airbrake-ruby/file_cache.rb +48 -0
- data/lib/airbrake-ruby/filter_chain.rb +95 -0
- data/lib/airbrake-ruby/filters/context_filter.rb +29 -0
- data/lib/airbrake-ruby/filters/dependency_filter.rb +31 -0
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +45 -0
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +33 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +90 -0
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +42 -0
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +66 -0
- data/lib/airbrake-ruby/filters/keys_blacklist.rb +50 -0
- data/lib/airbrake-ruby/filters/keys_filter.rb +140 -0
- data/lib/airbrake-ruby/filters/keys_whitelist.rb +49 -0
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +28 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +104 -0
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +23 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +92 -0
- data/lib/airbrake-ruby/hash_keyable.rb +37 -0
- data/lib/airbrake-ruby/ignorable.rb +44 -0
- data/lib/airbrake-ruby/nested_exception.rb +39 -0
- data/lib/airbrake-ruby/notice.rb +165 -0
- data/lib/airbrake-ruby/notice_notifier.rb +228 -0
- data/lib/airbrake-ruby/performance_notifier.rb +161 -0
- data/lib/airbrake-ruby/promise.rb +99 -0
- data/lib/airbrake-ruby/response.rb +71 -0
- data/lib/airbrake-ruby/stat.rb +56 -0
- data/lib/airbrake-ruby/sync_sender.rb +111 -0
- data/lib/airbrake-ruby/tdigest.rb +393 -0
- data/lib/airbrake-ruby/time_truncate.rb +17 -0
- data/lib/airbrake-ruby/truncator.rb +115 -0
- data/lib/airbrake-ruby/version.rb +6 -0
- data/spec/airbrake_spec.rb +171 -0
- data/spec/async_sender_spec.rb +154 -0
- data/spec/backtrace_spec.rb +438 -0
- data/spec/code_hunk_spec.rb +118 -0
- data/spec/config/validator_spec.rb +189 -0
- data/spec/config_spec.rb +281 -0
- data/spec/deploy_notifier_spec.rb +41 -0
- data/spec/file_cache.rb +36 -0
- data/spec/filter_chain_spec.rb +83 -0
- data/spec/filters/context_filter_spec.rb +25 -0
- data/spec/filters/dependency_filter_spec.rb +14 -0
- data/spec/filters/exception_attributes_filter_spec.rb +63 -0
- data/spec/filters/gem_root_filter_spec.rb +44 -0
- data/spec/filters/git_last_checkout_filter_spec.rb +48 -0
- data/spec/filters/git_repository_filter.rb +53 -0
- data/spec/filters/git_revision_filter_spec.rb +126 -0
- data/spec/filters/keys_blacklist_spec.rb +236 -0
- data/spec/filters/keys_whitelist_spec.rb +205 -0
- data/spec/filters/root_directory_filter_spec.rb +42 -0
- data/spec/filters/sql_filter_spec.rb +219 -0
- data/spec/filters/system_exit_filter_spec.rb +14 -0
- data/spec/filters/thread_filter_spec.rb +279 -0
- data/spec/fixtures/notroot.txt +7 -0
- data/spec/fixtures/project_root/code.rb +221 -0
- data/spec/fixtures/project_root/empty_file.rb +0 -0
- data/spec/fixtures/project_root/long_line.txt +1 -0
- data/spec/fixtures/project_root/short_file.rb +3 -0
- data/spec/fixtures/project_root/vendor/bundle/ignored_file.rb +5 -0
- data/spec/helpers.rb +9 -0
- data/spec/ignorable_spec.rb +14 -0
- data/spec/nested_exception_spec.rb +75 -0
- data/spec/notice_notifier_spec.rb +436 -0
- data/spec/notice_notifier_spec/options_spec.rb +266 -0
- data/spec/notice_spec.rb +297 -0
- data/spec/performance_notifier_spec.rb +287 -0
- data/spec/promise_spec.rb +165 -0
- data/spec/response_spec.rb +82 -0
- data/spec/spec_helper.rb +102 -0
- data/spec/stat_spec.rb +35 -0
- data/spec/sync_sender_spec.rb +140 -0
- data/spec/tdigest_spec.rb +230 -0
- data/spec/time_truncate_spec.rb +13 -0
- data/spec/truncator_spec.rb +238 -0
- metadata +278 -0
@@ -0,0 +1,266 @@
|
|
1
|
+
RSpec.describe Airbrake::NoticeNotifier do
|
2
|
+
def expect_a_request_with_body(body)
|
3
|
+
expect(a_request(:post, endpoint).with(body: body)).to have_been_made.once
|
4
|
+
end
|
5
|
+
|
6
|
+
let(:project_id) { 105138 }
|
7
|
+
let(:project_key) { 'fd04e13d806a90f96614ad8e529b2822' }
|
8
|
+
let(:localhost) { 'http://localhost:8080' }
|
9
|
+
|
10
|
+
let(:endpoint) do
|
11
|
+
"https://api.airbrake.io/api/v3/projects/#{project_id}/notices"
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:user_params) do
|
15
|
+
{ project_id: project_id,
|
16
|
+
project_key: project_key,
|
17
|
+
logger: Logger.new(StringIO.new) }
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:params) { {} }
|
21
|
+
let(:ex) { AirbrakeTestError.new }
|
22
|
+
let(:config) { Airbrake::Config.new(user_params.merge(params)) }
|
23
|
+
|
24
|
+
subject { described_class.new(config) }
|
25
|
+
|
26
|
+
before do
|
27
|
+
stub_request(:post, endpoint).to_return(status: 201, body: '{}')
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "options" do
|
31
|
+
describe ":host" do
|
32
|
+
context "when custom" do
|
33
|
+
shared_examples 'endpoint' do |host, endpoint, title|
|
34
|
+
let(:params) { { host: host } }
|
35
|
+
|
36
|
+
example(title) do
|
37
|
+
stub_request(:post, endpoint).to_return(status: 201, body: '{}')
|
38
|
+
subject.notify_sync(ex)
|
39
|
+
|
40
|
+
expect(a_request(:post, endpoint)).to have_been_made.once
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
path = '/api/v3/projects/105138/notices'
|
45
|
+
|
46
|
+
context "given a full host" do
|
47
|
+
include_examples('endpoint', localhost = 'http://localhost:8080',
|
48
|
+
URI.join(localhost, path),
|
49
|
+
"sends notices to the specified host's endpoint")
|
50
|
+
end
|
51
|
+
|
52
|
+
context "given a full host" do
|
53
|
+
include_examples('endpoint', localhost = 'http://localhost',
|
54
|
+
URI.join(localhost, path),
|
55
|
+
"assumes port 80 by default")
|
56
|
+
end
|
57
|
+
|
58
|
+
context "given a host without scheme" do
|
59
|
+
include_examples 'endpoint', localhost = 'localhost:8080',
|
60
|
+
URI.join("https://#{localhost}", path),
|
61
|
+
"assumes https by default"
|
62
|
+
end
|
63
|
+
|
64
|
+
context "given only hostname" do
|
65
|
+
include_examples 'endpoint', localhost = 'localhost',
|
66
|
+
URI.join("https://#{localhost}", path),
|
67
|
+
"assumes https and port 80 by default"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe ":root_directory" do
|
73
|
+
let(:params) { { root_directory: '/home/kyrylo/code' } }
|
74
|
+
|
75
|
+
it "filters out frames" do
|
76
|
+
airbrake = described_class.new(config)
|
77
|
+
airbrake.notify_sync(ex)
|
78
|
+
|
79
|
+
expect(
|
80
|
+
a_request(:post, endpoint).
|
81
|
+
with(body: %r|{"file":"/PROJECT_ROOT/airbrake/ruby/spec/airbrake_spec.+|)
|
82
|
+
).to have_been_made.once
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when present and is a" do
|
86
|
+
shared_examples 'root directory' do |dir|
|
87
|
+
let(:params) { { root_directory: dir } }
|
88
|
+
|
89
|
+
it "being included into the notice's payload" do
|
90
|
+
subject.notify_sync(ex)
|
91
|
+
expect(
|
92
|
+
a_request(:post, endpoint).
|
93
|
+
with(body: %r{"rootDirectory":"/bingo/bango"})
|
94
|
+
).to have_been_made.once
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "String" do
|
99
|
+
include_examples 'root directory', '/bingo/bango'
|
100
|
+
end
|
101
|
+
|
102
|
+
context "Pathname" do
|
103
|
+
include_examples 'root directory', Pathname.new('/bingo/bango')
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe ":proxy" do
|
109
|
+
let(:proxy) do
|
110
|
+
WEBrick::HTTPServer.new(
|
111
|
+
Port: 0,
|
112
|
+
Logger: WEBrick::Log.new('/dev/null'),
|
113
|
+
AccessLog: []
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
let(:requests) { Queue.new }
|
118
|
+
|
119
|
+
let(:proxy_params) do
|
120
|
+
{ host: 'localhost',
|
121
|
+
port: proxy.config[:Port],
|
122
|
+
user: 'user',
|
123
|
+
password: 'password' }
|
124
|
+
end
|
125
|
+
|
126
|
+
let(:params) do
|
127
|
+
{
|
128
|
+
proxy: proxy_params,
|
129
|
+
host: "http://localhost:#{proxy.config[:Port]}"
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
before do
|
134
|
+
proxy.mount_proc '/' do |req, res|
|
135
|
+
requests << req
|
136
|
+
res.status = 201
|
137
|
+
res.body = "OK\n"
|
138
|
+
end
|
139
|
+
|
140
|
+
Thread.new { proxy.start }
|
141
|
+
end
|
142
|
+
|
143
|
+
after { proxy.stop }
|
144
|
+
|
145
|
+
it "is being used if configured" do
|
146
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.6.0")
|
147
|
+
skip(
|
148
|
+
"We use Webmock 2, which doesn't support Ruby 2.6+. It's " \
|
149
|
+
"safe to run this test on 2.6+ once we upgrade to Webmock 3.5+"
|
150
|
+
)
|
151
|
+
end
|
152
|
+
subject.notify_sync(ex)
|
153
|
+
|
154
|
+
proxied_request = requests.pop(true)
|
155
|
+
|
156
|
+
expect(proxied_request.header['proxy-authorization'].first).
|
157
|
+
to eq('Basic dXNlcjpwYXNzd29yZA==')
|
158
|
+
|
159
|
+
# rubocop:disable Metrics/LineLength
|
160
|
+
expect(proxied_request.request_line).
|
161
|
+
to eq("POST http://localhost:#{proxy.config[:Port]}/api/v3/projects/105138/notices HTTP/1.1\r\n")
|
162
|
+
# rubocop:enable Metrics/LineLength
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe ":environment" do
|
167
|
+
context "when present" do
|
168
|
+
let(:params) { { environment: :production } }
|
169
|
+
|
170
|
+
it "being included into the notice's payload" do
|
171
|
+
subject.notify_sync(ex)
|
172
|
+
expect(
|
173
|
+
a_request(:post, endpoint).
|
174
|
+
with(body: /"context":{.*"environment":"production".*}/)
|
175
|
+
).to have_been_made.once
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe ":ignore_environments" do
|
181
|
+
shared_examples 'sent notice' do |params|
|
182
|
+
let(:params) { params }
|
183
|
+
|
184
|
+
it "sends a notice" do
|
185
|
+
subject.notify_sync(ex)
|
186
|
+
expect(a_request(:post, endpoint)).to have_been_made
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
shared_examples 'ignored notice' do |params|
|
191
|
+
let(:params) { params }
|
192
|
+
|
193
|
+
it "ignores exceptions occurring in envs that were not configured" do
|
194
|
+
subject.notify_sync(ex)
|
195
|
+
expect(a_request(:post, endpoint)).not_to have_been_made
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context "when env is set and ignore_environments doesn't mention it" do
|
200
|
+
params = {
|
201
|
+
environment: :development,
|
202
|
+
ignore_environments: [:production]
|
203
|
+
}
|
204
|
+
|
205
|
+
include_examples 'sent notice', params
|
206
|
+
end
|
207
|
+
|
208
|
+
context "when the current env and notify envs are the same" do
|
209
|
+
params = {
|
210
|
+
environment: :development,
|
211
|
+
ignore_environments: %i[production development]
|
212
|
+
}
|
213
|
+
|
214
|
+
include_examples 'ignored notice', params
|
215
|
+
|
216
|
+
it "returns early and doesn't try to parse the given exception" do
|
217
|
+
expect(Airbrake::Notice).not_to receive(:new)
|
218
|
+
expect(subject.notify_sync(ex)).
|
219
|
+
to eq('error' => "The 'development' environment is ignored")
|
220
|
+
expect(a_request(:post, endpoint)).not_to have_been_made
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "when the current env is not set and notify envs are present" do
|
225
|
+
params = { ignore_environments: %i[production development] }
|
226
|
+
|
227
|
+
include_examples 'sent notice', params
|
228
|
+
end
|
229
|
+
|
230
|
+
context "when the current env is set and notify envs aren't" do
|
231
|
+
include_examples 'sent notice', environment: :development
|
232
|
+
end
|
233
|
+
|
234
|
+
context "when ignore_environments specifies a Regexp pattern" do
|
235
|
+
params = {
|
236
|
+
environment: :testing,
|
237
|
+
ignore_environments: ['staging', /test.+/]
|
238
|
+
}
|
239
|
+
|
240
|
+
include_examples 'ignored notice', params
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe ":blacklist_keys" do
|
245
|
+
# Fixes https://github.com/airbrake/airbrake-ruby/issues/276
|
246
|
+
context "when specified along with :whitelist_keys" do
|
247
|
+
context "and when context payload is present" do
|
248
|
+
let(:params) do
|
249
|
+
{
|
250
|
+
blacklist_keys: %i[password password_confirmation],
|
251
|
+
whitelist_keys: [:email, /user/i, 'account_id']
|
252
|
+
}
|
253
|
+
end
|
254
|
+
|
255
|
+
it "sends a notice" do
|
256
|
+
notice = subject.build_notice(ex)
|
257
|
+
notice[:context][:headers] = 'banana'
|
258
|
+
subject.notify_sync(notice)
|
259
|
+
|
260
|
+
expect(a_request(:post, endpoint)).to have_been_made
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
data/spec/notice_spec.rb
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
RSpec.describe Airbrake::Notice do
|
2
|
+
let(:notice) do
|
3
|
+
described_class.new(Airbrake::Config.new, AirbrakeTestError.new, bingo: '1')
|
4
|
+
end
|
5
|
+
|
6
|
+
describe "#to_json" do
|
7
|
+
context "app_version" do
|
8
|
+
context "when missing" do
|
9
|
+
it "doesn't include app_version" do
|
10
|
+
expect(notice.to_json).not_to match(/"context":{"version":"1.2.3"/)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when present" do
|
15
|
+
let(:config) do
|
16
|
+
Airbrake::Config.new(app_version: '1.2.3', root_directory: '/one/two')
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:notice) { described_class.new(config, AirbrakeTestError.new) }
|
20
|
+
|
21
|
+
it "includes app_version" do
|
22
|
+
expect(notice.to_json).to match(/"context":{"version":"1.2.3"/)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "includes root_directory" do
|
26
|
+
expect(notice.to_json).to match(%r{"rootDirectory":"/one/two"})
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when versions is empty" do
|
32
|
+
it "doesn't set the 'versions' payload" do
|
33
|
+
expect(notice.to_json).not_to match(
|
34
|
+
/"context":{"versions":{"dep":"1.2.3"}}/
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when versions is not empty" do
|
40
|
+
it "sets the 'versions' payload" do
|
41
|
+
notice[:context][:versions] = { 'dep' => '1.2.3' }
|
42
|
+
expect(notice.to_json).to match(
|
43
|
+
/"context":{.*"versions":{"dep":"1.2.3"}.*}/
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "truncation" do
|
49
|
+
shared_examples 'payloads' do |size, msg|
|
50
|
+
it msg do
|
51
|
+
ex = AirbrakeTestError.new
|
52
|
+
|
53
|
+
backtrace = []
|
54
|
+
size.times { backtrace << "bin/rails:3:in `<main>'" }
|
55
|
+
ex.set_backtrace(backtrace)
|
56
|
+
|
57
|
+
notice = described_class.new(Airbrake::Config.new, ex)
|
58
|
+
|
59
|
+
expect(notice.to_json.bytesize).to be < 64000
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
max_msg = 'truncates to the max allowed size'
|
64
|
+
|
65
|
+
context "with an extremely huge payload" do
|
66
|
+
include_examples 'payloads', 200_000, max_msg
|
67
|
+
end
|
68
|
+
|
69
|
+
context "with a big payload" do
|
70
|
+
include_examples 'payloads', 50_000, max_msg
|
71
|
+
end
|
72
|
+
|
73
|
+
small_msg = "doesn't truncate it"
|
74
|
+
|
75
|
+
context "with a small payload" do
|
76
|
+
include_examples 'payloads', 1000, small_msg
|
77
|
+
end
|
78
|
+
|
79
|
+
context "with a tiny payload" do
|
80
|
+
include_examples 'payloads', 300, small_msg
|
81
|
+
end
|
82
|
+
|
83
|
+
context "when truncation failed" do
|
84
|
+
it "returns nil" do
|
85
|
+
expect_any_instance_of(Airbrake::Truncator).
|
86
|
+
to receive(:reduce_max_size).and_return(0)
|
87
|
+
|
88
|
+
encoded = Base64.encode64("\xD3\xE6\xBC\x9D\xBA").encode!('ASCII-8BIT')
|
89
|
+
bad_string = Base64.decode64(encoded)
|
90
|
+
|
91
|
+
ex = AirbrakeTestError.new
|
92
|
+
|
93
|
+
backtrace = []
|
94
|
+
10.times { backtrace << "bin/rails:3:in `<#{bad_string}>'" }
|
95
|
+
ex.set_backtrace(backtrace)
|
96
|
+
|
97
|
+
config = Airbrake::Config.new(logger: Logger.new('/dev/null'))
|
98
|
+
notice = described_class.new(config, ex)
|
99
|
+
|
100
|
+
expect(notice.to_json).to be_nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "object replacement with its string version" do
|
105
|
+
let(:klass) { Class.new {} }
|
106
|
+
let(:ex) { AirbrakeTestError.new }
|
107
|
+
let(:params) { { bingo: [Object.new, klass.new] } }
|
108
|
+
let(:notice) { described_class.new(Airbrake::Config.new, ex, params) }
|
109
|
+
|
110
|
+
before do
|
111
|
+
backtrace = []
|
112
|
+
backtrace_size.times { backtrace << "bin/rails:3:in `<main>'" }
|
113
|
+
ex.set_backtrace(backtrace)
|
114
|
+
end
|
115
|
+
|
116
|
+
context "with payload within the limits" do
|
117
|
+
let(:backtrace_size) { 1000 }
|
118
|
+
|
119
|
+
it "doesn't happen" do
|
120
|
+
expect(notice.to_json).
|
121
|
+
to match(/bingo":\["#<Object:.+>","#<#<Class:.+>:.+>"/)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context "with payload bigger than the limit" do
|
126
|
+
context "with payload within the limits" do
|
127
|
+
let(:backtrace_size) { 50_000 }
|
128
|
+
|
129
|
+
it "happens" do
|
130
|
+
expect(notice.to_json).
|
131
|
+
to match(/bingo":\[".+Object.+",".+Class.+"/)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "given a closed IO object" do
|
139
|
+
context "and when it is not monkey-patched by ActiveSupport" do
|
140
|
+
it "is not getting truncated" do
|
141
|
+
notice[:params] = { obj: IO.new(0).tap(&:close) }
|
142
|
+
expect(notice.to_json).to match(/"obj":"#<IO:0x.+>"/)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "and when it is monkey-patched by ActiveSupport" do
|
147
|
+
# Instances of this class contain a closed IO object assigned to an
|
148
|
+
# instance variable. Normally, the JSON gem, which we depend on can
|
149
|
+
# parse closed IO objects. However, because ActiveSupport monkey-patches
|
150
|
+
# #to_json and calls #to_a on them, they raise IOError when we try to
|
151
|
+
# serialize them.
|
152
|
+
#
|
153
|
+
# @see https://goo.gl/0A3xNC
|
154
|
+
class ObjectWithIoIvars
|
155
|
+
def initialize
|
156
|
+
@bongo = Tempfile.new('bongo').tap(&:close)
|
157
|
+
end
|
158
|
+
|
159
|
+
# @raise [NotImplementedError] when inside a Rails environment
|
160
|
+
def to_json(*)
|
161
|
+
raise NotImplementedError
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# @see ObjectWithIoIvars
|
166
|
+
class ObjectWithNestedIoIvars
|
167
|
+
def initialize
|
168
|
+
@bish = ObjectWithIoIvars.new
|
169
|
+
end
|
170
|
+
|
171
|
+
# @see ObjectWithIoIvars#to_json
|
172
|
+
def to_json(*)
|
173
|
+
raise NotImplementedError
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "and also when it's a closed Tempfile" do
|
178
|
+
it "doesn't fail" do
|
179
|
+
notice[:params] = { obj: Tempfile.new('bongo').tap(&:close) }
|
180
|
+
expect(notice.to_json).to match(/"obj":"#<(Temp)?file:0x.+>"/i)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "and also when it's an IO ivar" do
|
185
|
+
it "doesn't fail" do
|
186
|
+
notice[:params] = { obj: ObjectWithIoIvars.new }
|
187
|
+
expect(notice.to_json).to match(/"obj":".+ObjectWithIoIvars.+"/)
|
188
|
+
end
|
189
|
+
|
190
|
+
context "and when it's deeply nested inside a hash" do
|
191
|
+
it "doesn't fail" do
|
192
|
+
notice[:params] = { a: { b: { c: ObjectWithIoIvars.new } } }
|
193
|
+
expect(notice.to_json).to match(
|
194
|
+
/"params":{"a":{"b":{"c":".+ObjectWithIoIvars.+"}}.*}/
|
195
|
+
)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context "and when it's deeply nested inside an array" do
|
200
|
+
it "doesn't fail" do
|
201
|
+
notice[:params] = { a: [[ObjectWithIoIvars.new]] }
|
202
|
+
expect(notice.to_json).to match(
|
203
|
+
/"params":{"a":\[\[".+ObjectWithIoIvars.+"\]\].*}/
|
204
|
+
)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "and also when it's a non-IO ivar, which contains an IO ivar itself" do
|
210
|
+
it "doesn't fail" do
|
211
|
+
notice[:params] = { obj: ObjectWithNestedIoIvars.new }
|
212
|
+
expect(notice.to_json).to match(/"obj":".+ObjectWithNested.+"/)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
it "overwrites the 'notifier' payload with the default values" do
|
219
|
+
notice[:notifier] = { name: 'bingo', bango: 'bongo' }
|
220
|
+
|
221
|
+
expect(notice.to_json).
|
222
|
+
to match(/"notifier":{"name":"airbrake-ruby","version":".+","url":".+"}/)
|
223
|
+
end
|
224
|
+
|
225
|
+
it "always contains context/hostname" do
|
226
|
+
expect(notice.to_json).
|
227
|
+
to match(/"context":{.*"hostname":".+".*}/)
|
228
|
+
end
|
229
|
+
|
230
|
+
it "defaults to the error severity" do
|
231
|
+
expect(notice.to_json).to match(/"context":{.*"severity":"error".*}/)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "always contains environment/program_name" do
|
235
|
+
expect(notice.to_json).
|
236
|
+
to match(%r|"environment":{"program_name":.+/rspec.*|)
|
237
|
+
end
|
238
|
+
|
239
|
+
it "contains errors" do
|
240
|
+
expect(notice.to_json).
|
241
|
+
to match(/"errors":\[{"type":"AirbrakeTestError","message":"App crash/)
|
242
|
+
end
|
243
|
+
|
244
|
+
it "contains a backtrace" do
|
245
|
+
expect(notice.to_json).
|
246
|
+
to match(%r|"backtrace":\[{"file":"/home/.+/spec/spec_helper.rb"|)
|
247
|
+
end
|
248
|
+
|
249
|
+
it "contains params" do
|
250
|
+
expect(notice.to_json).to match(/"params":{"bingo":"1"}/)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe "#[]" do
|
255
|
+
it "accesses payload" do
|
256
|
+
expect(notice[:params]).to eq(bingo: '1')
|
257
|
+
end
|
258
|
+
|
259
|
+
it "raises error if notice is ignored" do
|
260
|
+
notice.ignore!
|
261
|
+
expect { notice[:params] }.
|
262
|
+
to raise_error(Airbrake::Error, 'cannot access ignored Airbrake::Notice')
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe "#[]=" do
|
267
|
+
it "sets a payload value" do
|
268
|
+
hash = { bingo: 'bango' }
|
269
|
+
notice[:params] = hash
|
270
|
+
expect(notice[:params]).to equal(hash)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "raises error if notice is ignored" do
|
274
|
+
notice.ignore!
|
275
|
+
expect { notice[:params] = {} }.
|
276
|
+
to raise_error(Airbrake::Error, 'cannot access ignored Airbrake::Notice')
|
277
|
+
end
|
278
|
+
|
279
|
+
it "raises error when trying to assign unrecognized key" do
|
280
|
+
expect { notice[:bingo] = 1 }.
|
281
|
+
to raise_error(Airbrake::Error, /:bingo is not recognized among/)
|
282
|
+
end
|
283
|
+
|
284
|
+
it "raises when setting non-hash objects as the value" do
|
285
|
+
expect { notice[:params] = Object.new }.
|
286
|
+
to raise_error(Airbrake::Error, 'Got Object value, wanted a Hash')
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
describe "#stash" do
|
291
|
+
it "returns a hash" do
|
292
|
+
obj = Object.new
|
293
|
+
notice.stash[:bingo_object] = obj
|
294
|
+
expect(notice.stash[:bingo_object]).to eql(obj)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|